diff --git a/addons/face/ui/src/ctx.jsx b/addons/face/ui/src/ctx.jsx index 92c318f7ff..0a21f7ef57 100644 --- a/addons/face/ui/src/ctx.jsx +++ b/addons/face/ui/src/ctx.jsx @@ -1,4 +1,4 @@ -import en from './i18n/en.js' +import { locales } from './i18n/index.js' import { modelFrom as pickModel } from './lib/model.js' // The one object every page factory of the Face add-on receives. It binds the @@ -8,12 +8,25 @@ export function createContext(sdk) { const { React } = sdk const h = React.createElement const Fragment = React.Fragment - sdk.locale.define('en', Object.fromEntries(Object.entries(en).filter(([, v]) => typeof v === 'string'))) - const lists = Object.fromEntries(Object.entries(en).filter(([, v]) => Array.isArray(v))) + // Every language of the page; a key a language lacks reads in English. + for (const [language, bundle] of Object.entries(locales)) sdk.locale.define(language, bundle) + const en = locales.en const t = (key, vars) => sdk.locale.t(key, key, vars) - // A list of strings or objects (the "why" and "stored" notes). - const list = (key) => lists[key] || [] + // A list of strings or objects (the "why" and "stored" notes). The bundles hold + // each item under a numbered key (key.0, or key.0.field for an object), so + // that every item is translated like any other text. + const list = (key) => { + const out = [] + for (let i = 0; ; i++) { + const own = `${key}.${i}` + if (typeof en[own] === 'string') { out.push(t(own)); continue } + const prefix = `${own}.` + const fields = Object.keys(en).filter((k) => k.startsWith(prefix)) + if (fields.length === 0) return out + out.push(Object.fromEntries(fields.map((k) => [k.slice(prefix.length), t(k)]))) + } + } // A link to a page of this add-on, as an anchor the shell navigates. function Link({ page, query, className, children, ...rest }) { diff --git a/addons/face/ui/src/i18n/face.de.json b/addons/face/ui/src/i18n/face.de.json new file mode 100644 index 0000000000..16044fc6c0 --- /dev/null +++ b/addons/face/ui/src/i18n/face.de.json @@ -0,0 +1,187 @@ +{ + "faces.title": "Gesichter", + "faces.lede": "Find out whose face is in a photo, or whether two photos show the same person. Photos go only to the model running on this server.", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "In Arbeit", + "face.person": "person", + "face.print": "faceprint", + "face.whoTab": "Who is this", + "face.whoLede": "Matches a photo against the people you enrolled.", + "face.sameLede": "Two photos, one question. Nobody has to be enrolled.", + "face.whoGo": "Find who this is", + "face.working": "Sucht", + "face.whoNote": "Searches the people enrolled on this server. The photo you test with is not stored.", + "face.sameNote": "The answer comes from one faceprint per photo. Nothing is stored.", + "face.workingText": "Comparing the photo with the faceprints the server holds.", + "face.probably": "Probably {{name}}", + "face.matchText": "{{name}}'s faceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "face.nobody": "Nobody enrolled looks like this", + "face.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new person, or a photo that is blurred, turned away or in poor light.", + "face.noneEnrolled": "Nobody is enrolled to compare with", + "face.noneEnrolledText": "Enrol a person first, then look up a photo against them.", + "face.noneReturned": "The server returned no people", + "face.noneReturnedText": "It holds no faceprints made with this model. If the server restarted, enrolled faces are gone: enrol them again.", + "face.enrolThis": "Enrol this face", + "face.same": "Probably the same person", + "face.different": "Probably different people", + "face.knownTitle": "Known people", + "face.enrol": "Enrol a person", + "face.enrolTitle": "Enrol a person", + "face.enrolIntro": "The server keeps a faceprint and a name. It does not keep the photo.", + "face.emptyTitle": "Nobody enrolled in this browser yet", + "face.emptyText": "Enrol a person with one clear photo, then test a photo against them.", + "face.agreedHint": "I have their permission, or another lawful basis, to store a faceprint of them.", + "face.keepHint": "Lets you see the photo and enrol again after a server restart. Off by default. It is stored in this browser only.", + "face.antiSpoof": "Check for photos of photos", + "face.antiSpoofHint": "Needs a model that supports it.", + "face.real": "Real", + "face.spoof": "Spoof", + "face.found": "Face in photo {{n}}", + "face.neededTitle": "Faces need a face model", + "face.neededText": "Face recognition turns a photo into a faceprint and compares faceprints. It runs on a face-recognition model, and none is installed on this server.", + "face.neededWhat": "Install a model that supports face recognition. Once one is installed, this page turns on by itself. Users also need the Face recognition permission.", + "face.moreTitle": "More tools", + "face.moreHint": "detect faces, guess traits, get the raw faceprint", + "face.why.0": "The server matches one face per photo. A group photo is matched on a single face.", + "face.why.1": "Light, angle, glasses and blur change a face a lot.", + "face.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "face.stored.0.where": "On the server", + "face.stored.0.what": "A {{print}} (numbers that describe the face, not the picture), a name and your labels. They are kept in memory and are lost when the server restarts. The photo is not kept.", + "face.stored.1.where": "In this browser", + "face.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the photo only if you tick Keep a copy when enrolling.", + "face.stored.2.where": "Photos you test with", + "face.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Record", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "Entfernen", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "Abbrechen", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "Stopp", + "clip.start": "Start recording", + "clip.recorded": "Recording", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "Vorschau", + "clip.photoAlt": "The photo you chose", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "Name", + "known.added": "Hinzugefügt", + "known.actions": "Aktionen", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "Rückgängig", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "{{name}} entfernen", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "stored.title": "What is stored, and where", + "enrol.close": "Schließen", + "enrol.cancel": "Abbrechen", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Recording", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Recording", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "Name", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Exact transcript", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "Erneut versuchen", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "Installieren", + "needed.installing": "Wird installiert", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Browse all models", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "off.face.title": "Face recognition is off for your account", + "off.face.text": "An administrator has not allowed you to use face recognition.", + "off.face.what": "An administrator can turn on the Face recognition permission for your user in Users and keys." +} diff --git a/addons/face/ui/src/i18n/en.js b/addons/face/ui/src/i18n/face.en.json similarity index 91% rename from addons/face/ui/src/i18n/en.js rename to addons/face/ui/src/i18n/face.en.json index efa468ebb7..3db65a6b48 100644 --- a/addons/face/ui/src/i18n/en.js +++ b/addons/face/ui/src/i18n/face.en.json @@ -1,5 +1,4 @@ -// Generated once from the host locale files; edit by hand from here on. -export default { +{ "faces.title": "Faces", "faces.lede": "Find out whose face is in a photo, or whether two photos show the same person. Photos go only to the model running on this server.", "tool.aria": "Recognition tool", @@ -52,25 +51,15 @@ export default { "face.neededWhat": "Install a model that supports face recognition. Once one is installed, this page turns on by itself. Users also need the Face recognition permission.", "face.moreTitle": "More tools", "face.moreHint": "detect faces, guess traits, get the raw faceprint", - "face.why": [ - "The server matches one face per photo. A group photo is matched on a single face.", - "Light, angle, glasses and blur change a face a lot.", - "This is a similarity score, not a security check. Do not use it to unlock anything." - ], - "face.stored": [ - { - "where": "On the server", - "what": "A {{print}} (numbers that describe the face, not the picture), a name and your labels. They are kept in memory and are lost when the server restarts. The photo is not kept." - }, - { - "where": "In this browser", - "what": "The name, labels and time of each person you enrolled. A copy of the photo only if you tick Keep a copy when enrolling." - }, - { - "where": "Photos you test with", - "what": "They go to the model on this server to be compared and are not kept." - } - ], + "face.why.0": "The server matches one face per photo. A group photo is matched on a single face.", + "face.why.1": "Light, angle, glasses and blur change a face a lot.", + "face.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "face.stored.0.where": "On the server", + "face.stored.0.what": "A {{print}} (numbers that describe the face, not the picture), a name and your labels. They are kept in memory and are lost when the server restarts. The photo is not kept.", + "face.stored.1.where": "In this browser", + "face.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the photo only if you tick Keep a copy when enrolling.", + "face.stored.2.where": "Photos you test with", + "face.stored.2.what": "They go to the model on this server to be compared and are not kept.", "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", "same.distance": "distance", "word.strong": "Strong match", diff --git a/addons/face/ui/src/i18n/face.es.json b/addons/face/ui/src/i18n/face.es.json new file mode 100644 index 0000000000..3c25829f76 --- /dev/null +++ b/addons/face/ui/src/i18n/face.es.json @@ -0,0 +1,187 @@ +{ + "faces.title": "Rostros", + "faces.lede": "Find out whose face is in a photo, or whether two photos show the same person. Photos go only to the model running on this server.", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "En marcha", + "face.person": "person", + "face.print": "faceprint", + "face.whoTab": "Who is this", + "face.whoLede": "Matches a photo against the people you enrolled.", + "face.sameLede": "Two photos, one question. Nobody has to be enrolled.", + "face.whoGo": "Find who this is", + "face.working": "Buscando", + "face.whoNote": "Searches the people enrolled on this server. The photo you test with is not stored.", + "face.sameNote": "The answer comes from one faceprint per photo. Nothing is stored.", + "face.workingText": "Comparing the photo with the faceprints the server holds.", + "face.probably": "Probably {{name}}", + "face.matchText": "{{name}}'s faceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "face.nobody": "Nobody enrolled looks like this", + "face.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new person, or a photo that is blurred, turned away or in poor light.", + "face.noneEnrolled": "Nobody is enrolled to compare with", + "face.noneEnrolledText": "Enrol a person first, then look up a photo against them.", + "face.noneReturned": "The server returned no people", + "face.noneReturnedText": "It holds no faceprints made with this model. If the server restarted, enrolled faces are gone: enrol them again.", + "face.enrolThis": "Enrol this face", + "face.same": "Probably the same person", + "face.different": "Probably different people", + "face.knownTitle": "Known people", + "face.enrol": "Enrol a person", + "face.enrolTitle": "Enrol a person", + "face.enrolIntro": "The server keeps a faceprint and a name. It does not keep the photo.", + "face.emptyTitle": "Nobody enrolled in this browser yet", + "face.emptyText": "Enrol a person with one clear photo, then test a photo against them.", + "face.agreedHint": "I have their permission, or another lawful basis, to store a faceprint of them.", + "face.keepHint": "Lets you see the photo and enrol again after a server restart. Off by default. It is stored in this browser only.", + "face.antiSpoof": "Check for photos of photos", + "face.antiSpoofHint": "Needs a model that supports it.", + "face.real": "Real", + "face.spoof": "Spoof", + "face.found": "Face in photo {{n}}", + "face.neededTitle": "Faces need a face model", + "face.neededText": "Face recognition turns a photo into a faceprint and compares faceprints. It runs on a face-recognition model, and none is installed on this server.", + "face.neededWhat": "Install a model that supports face recognition. Once one is installed, this page turns on by itself. Users also need the Face recognition permission.", + "face.moreTitle": "More tools", + "face.moreHint": "detect faces, guess traits, get the raw faceprint", + "face.why.0": "The server matches one face per photo. A group photo is matched on a single face.", + "face.why.1": "Light, angle, glasses and blur change a face a lot.", + "face.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "face.stored.0.where": "On the server", + "face.stored.0.what": "A {{print}} (numbers that describe the face, not the picture), a name and your labels. They are kept in memory and are lost when the server restarts. The photo is not kept.", + "face.stored.1.where": "In this browser", + "face.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the photo only if you tick Keep a copy when enrolling.", + "face.stored.2.where": "Photos you test with", + "face.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Record", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "Quitar", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "Cancelar", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "Detener", + "clip.start": "Start recording", + "clip.recorded": "Recording", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "Vista previa", + "clip.photoAlt": "The photo you chose", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "Nombre", + "known.added": "Añadido", + "known.actions": "Acciones", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "Deshacer", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "Quitar {{name}}", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "stored.title": "What is stored, and where", + "enrol.close": "Cerrar", + "enrol.cancel": "Cancelar", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Recording", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Recording", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "Nombre", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Exact transcript", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "Reintentar", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "Instalar", + "needed.installing": "Instalando", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Browse all models", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "off.face.title": "Face recognition is off for your account", + "off.face.text": "An administrator has not allowed you to use face recognition.", + "off.face.what": "An administrator can turn on the Face recognition permission for your user in Users and keys." +} diff --git a/addons/face/ui/src/i18n/face.fr.json b/addons/face/ui/src/i18n/face.fr.json new file mode 100644 index 0000000000..157840b265 --- /dev/null +++ b/addons/face/ui/src/i18n/face.fr.json @@ -0,0 +1,187 @@ +{ + "faces.title": "Visages", + "faces.lede": "Find out whose face is in a photo, or whether two photos show the same person. Photos go only to the model running on this server.", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "En cours", + "face.person": "person", + "face.print": "faceprint", + "face.whoTab": "Who is this", + "face.whoLede": "Matches a photo against the people you enrolled.", + "face.sameLede": "Two photos, one question. Nobody has to be enrolled.", + "face.whoGo": "Find who this is", + "face.working": "Recherche", + "face.whoNote": "Searches the people enrolled on this server. The photo you test with is not stored.", + "face.sameNote": "The answer comes from one faceprint per photo. Nothing is stored.", + "face.workingText": "Comparing the photo with the faceprints the server holds.", + "face.probably": "Probably {{name}}", + "face.matchText": "{{name}}'s faceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "face.nobody": "Nobody enrolled looks like this", + "face.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new person, or a photo that is blurred, turned away or in poor light.", + "face.noneEnrolled": "Nobody is enrolled to compare with", + "face.noneEnrolledText": "Enrol a person first, then look up a photo against them.", + "face.noneReturned": "The server returned no people", + "face.noneReturnedText": "It holds no faceprints made with this model. If the server restarted, enrolled faces are gone: enrol them again.", + "face.enrolThis": "Enrol this face", + "face.same": "Probably the same person", + "face.different": "Probably different people", + "face.knownTitle": "Known people", + "face.enrol": "Enrol a person", + "face.enrolTitle": "Enrol a person", + "face.enrolIntro": "The server keeps a faceprint and a name. It does not keep the photo.", + "face.emptyTitle": "Nobody enrolled in this browser yet", + "face.emptyText": "Enrol a person with one clear photo, then test a photo against them.", + "face.agreedHint": "I have their permission, or another lawful basis, to store a faceprint of them.", + "face.keepHint": "Lets you see the photo and enrol again after a server restart. Off by default. It is stored in this browser only.", + "face.antiSpoof": "Check for photos of photos", + "face.antiSpoofHint": "Needs a model that supports it.", + "face.real": "Real", + "face.spoof": "Spoof", + "face.found": "Face in photo {{n}}", + "face.neededTitle": "Faces need a face model", + "face.neededText": "Face recognition turns a photo into a faceprint and compares faceprints. It runs on a face-recognition model, and none is installed on this server.", + "face.neededWhat": "Install a model that supports face recognition. Once one is installed, this page turns on by itself. Users also need the Face recognition permission.", + "face.moreTitle": "More tools", + "face.moreHint": "detect faces, guess traits, get the raw faceprint", + "face.why.0": "The server matches one face per photo. A group photo is matched on a single face.", + "face.why.1": "Light, angle, glasses and blur change a face a lot.", + "face.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "face.stored.0.where": "On the server", + "face.stored.0.what": "A {{print}} (numbers that describe the face, not the picture), a name and your labels. They are kept in memory and are lost when the server restarts. The photo is not kept.", + "face.stored.1.where": "In this browser", + "face.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the photo only if you tick Keep a copy when enrolling.", + "face.stored.2.where": "Photos you test with", + "face.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Enregistrer", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "Retirer", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "Annuler", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "Arrêter", + "clip.start": "Démarrer l'enregistrement", + "clip.recorded": "Enregistrement", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "Aperçu", + "clip.photoAlt": "The photo you chose", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "Nom", + "known.added": "Ajouté", + "known.actions": "Actions", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "Annuler", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "Retirer {{name}}", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "stored.title": "What is stored, and where", + "enrol.close": "Fermer", + "enrol.cancel": "Annuler", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Enregistrement", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Enregistrement", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "Nom", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Transcription exacte", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "Réessayer", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "Installer", + "needed.installing": "Installation en cours", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Parcourir tous les modèles", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "off.face.title": "Face recognition is off for your account", + "off.face.text": "An administrator has not allowed you to use face recognition.", + "off.face.what": "An administrator can turn on the Face recognition permission for your user in Users and keys." +} diff --git a/addons/face/ui/src/i18n/face.id.json b/addons/face/ui/src/i18n/face.id.json new file mode 100644 index 0000000000..7f0e292cfc --- /dev/null +++ b/addons/face/ui/src/i18n/face.id.json @@ -0,0 +1,187 @@ +{ + "faces.title": "Wajah", + "faces.lede": "Find out whose face is in a photo, or whether two photos show the same person. Photos go only to the model running on this server.", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "Sedang dikerjakan", + "face.person": "person", + "face.print": "faceprint", + "face.whoTab": "Who is this", + "face.whoLede": "Matches a photo against the people you enrolled.", + "face.sameLede": "Two photos, one question. Nobody has to be enrolled.", + "face.whoGo": "Find who this is", + "face.working": "Mencari", + "face.whoNote": "Searches the people enrolled on this server. The photo you test with is not stored.", + "face.sameNote": "The answer comes from one faceprint per photo. Nothing is stored.", + "face.workingText": "Comparing the photo with the faceprints the server holds.", + "face.probably": "Probably {{name}}", + "face.matchText": "{{name}}'s faceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "face.nobody": "Nobody enrolled looks like this", + "face.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new person, or a photo that is blurred, turned away or in poor light.", + "face.noneEnrolled": "Nobody is enrolled to compare with", + "face.noneEnrolledText": "Enrol a person first, then look up a photo against them.", + "face.noneReturned": "The server returned no people", + "face.noneReturnedText": "It holds no faceprints made with this model. If the server restarted, enrolled faces are gone: enrol them again.", + "face.enrolThis": "Enrol this face", + "face.same": "Probably the same person", + "face.different": "Probably different people", + "face.knownTitle": "Known people", + "face.enrol": "Enrol a person", + "face.enrolTitle": "Enrol a person", + "face.enrolIntro": "The server keeps a faceprint and a name. It does not keep the photo.", + "face.emptyTitle": "Nobody enrolled in this browser yet", + "face.emptyText": "Enrol a person with one clear photo, then test a photo against them.", + "face.agreedHint": "I have their permission, or another lawful basis, to store a faceprint of them.", + "face.keepHint": "Lets you see the photo and enrol again after a server restart. Off by default. It is stored in this browser only.", + "face.antiSpoof": "Check for photos of photos", + "face.antiSpoofHint": "Needs a model that supports it.", + "face.real": "Real", + "face.spoof": "Spoof", + "face.found": "Face in photo {{n}}", + "face.neededTitle": "Faces need a face model", + "face.neededText": "Face recognition turns a photo into a faceprint and compares faceprints. It runs on a face-recognition model, and none is installed on this server.", + "face.neededWhat": "Install a model that supports face recognition. Once one is installed, this page turns on by itself. Users also need the Face recognition permission.", + "face.moreTitle": "More tools", + "face.moreHint": "detect faces, guess traits, get the raw faceprint", + "face.why.0": "The server matches one face per photo. A group photo is matched on a single face.", + "face.why.1": "Light, angle, glasses and blur change a face a lot.", + "face.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "face.stored.0.where": "On the server", + "face.stored.0.what": "A {{print}} (numbers that describe the face, not the picture), a name and your labels. They are kept in memory and are lost when the server restarts. The photo is not kept.", + "face.stored.1.where": "In this browser", + "face.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the photo only if you tick Keep a copy when enrolling.", + "face.stored.2.where": "Photos you test with", + "face.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Rekam", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "Hapus", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "Batal", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "Berhenti", + "clip.start": "Mulai merekam", + "clip.recorded": "Recording", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "Pratinjau", + "clip.photoAlt": "The photo you chose", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "Nama", + "known.added": "Ditambahkan", + "known.actions": "Aksi", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "Urungkan", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "Hapus {{name}}", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "stored.title": "What is stored, and where", + "enrol.close": "Tutup", + "enrol.cancel": "Batal", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Recording", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Recording", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "Nama", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Exact transcript", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "Coba lagi", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "Pasang", + "needed.installing": "Memasang", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Browse all models", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "off.face.title": "Face recognition is off for your account", + "off.face.text": "An administrator has not allowed you to use face recognition.", + "off.face.what": "An administrator can turn on the Face recognition permission for your user in Users and keys." +} diff --git a/addons/face/ui/src/i18n/face.it.json b/addons/face/ui/src/i18n/face.it.json new file mode 100644 index 0000000000..f3b6010053 --- /dev/null +++ b/addons/face/ui/src/i18n/face.it.json @@ -0,0 +1,187 @@ +{ + "faces.title": "Volti", + "faces.lede": "Scopri di chi è il volto in una foto, o se due foto mostrano la stessa persona. Le foto vanno solo al modello che gira su questo server.", + "tool.aria": "Strumento di riconoscimento", + "tool.sameTab": "Stessa persona?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Foto", + "tool.photoA": "Foto A", + "tool.photoB": "Foto B", + "tool.compare": "Confronta", + "tool.comparing": "Confronto in corso", + "tool.workingAria": "In corso", + "face.person": "persona", + "face.print": "impronta del volto", + "face.whoTab": "Chi è", + "face.whoLede": "Confronta una foto con le persone che hai registrato.", + "face.sameLede": "Due foto, una domanda. Non serve aver registrato nessuno.", + "face.whoGo": "Scopri chi è", + "face.working": "Cerca", + "face.whoNote": "Cerca tra le persone registrate su questo server. La foto che usi per il test non viene conservata.", + "face.sameNote": "La risposta viene da una sola impronta del volto per foto. Non viene conservato nulla.", + "face.workingText": "Confronto della foto con le impronte del volto che il server conserva.", + "face.probably": "Probabilmente {{name}}", + "face.matchText": "L'impronta del volto di {{name}} è la più vicina tra {{count}} confrontate, a distanza {{distance}}. Sotto {{cutoff}} si considera la stessa persona.", + "face.nobody": "Nessuno dei registrati assomiglia a questo volto", + "face.missText": "La più vicina era {{name}} a distanza {{distance}}, oltre la soglia di {{cutoff}}. Di solito significa una persona nuova, oppure una foto sfocata, di profilo o con poca luce.", + "face.noneEnrolled": "Non c'è nessuno registrato con cui confrontare", + "face.noneEnrolledText": "Registra prima una persona, poi cerca una foto rispetto a lei.", + "face.noneReturned": "Il server non ha restituito nessuna persona", + "face.noneReturnedText": "Non conserva impronte del volto create con questo modello. Se il server è stato riavviato, i volti registrati sono persi: registrali di nuovo.", + "face.enrolThis": "Registra questo volto", + "face.same": "Probabilmente la stessa persona", + "face.different": "Probabilmente persone diverse", + "face.knownTitle": "Persone note", + "face.enrol": "Registra una persona", + "face.enrolTitle": "Registra una persona", + "face.enrolIntro": "Il server conserva un'impronta del volto e un nome. Non conserva la foto.", + "face.emptyTitle": "Nessuno è ancora registrato in questo browser", + "face.emptyText": "Registra una persona con una foto nitida, poi prova una foto rispetto a lei.", + "face.agreedHint": "Ho il suo permesso, o un'altra base legittima, per conservare la sua impronta del volto.", + "face.keepHint": "Ti permette di vedere la foto e di registrare di nuovo dopo il riavvio del server. Disattivato per impostazione predefinita. Resta solo in questo browser.", + "face.antiSpoof": "Controlla le foto di foto", + "face.antiSpoofHint": "Serve un modello che lo supporti.", + "face.real": "Reale", + "face.spoof": "Falsa", + "face.found": "Volto nella foto {{n}}", + "face.neededTitle": "I volti richiedono un modello per i volti", + "face.neededText": "Il riconoscimento facciale trasforma una foto in un'impronta del volto e confronta le impronte. Funziona con un modello di riconoscimento facciale, e su questo server non ne è installato nessuno.", + "face.neededWhat": "Installa un modello che supporti il riconoscimento facciale. Appena ne è installato uno, questa pagina si attiva da sola. Gli utenti hanno bisogno anche del permesso di riconoscimento facciale.", + "face.moreTitle": "Altri strumenti", + "face.moreHint": "rileva volti, stima caratteristiche, ottieni l'impronta del volto grezza", + "face.why.0": "Il server confronta un solo volto per foto. In una foto di gruppo viene confrontato un solo volto.", + "face.why.1": "Luce, angolazione, occhiali e sfocatura cambiano molto un volto.", + "face.why.2": "È un punteggio di somiglianza, non un controllo di sicurezza. Non usarlo per sbloccare nulla.", + "face.stored.0.where": "Sul server", + "face.stored.0.what": "Un'{{print}} (numeri che descrivono il volto, non l'immagine), un nome e le tue etichette. Restano in memoria e si perdono al riavvio del server. La foto non viene conservata.", + "face.stored.1.where": "In questo browser", + "face.stored.1.what": "Nome, etichette e ora di ogni persona che hai registrato. Una copia della foto solo se spunti Conserva una copia durante la registrazione.", + "face.stored.2.where": "Foto che usi per il test", + "face.stored.2.what": "Vanno al modello su questo server per essere confrontate e non vengono conservate.", + "same.text": "Distanza tra le due impronte ({{print}}): {{distance}}. Sotto {{cutoff}} si considera la stessa persona.", + "same.distance": "distanza", + "word.strong": "Corrispondenza forte", + "word.likely": "Corrispondenza probabile", + "word.close": "Caso al limite", + "word.near": "Appena oltre la soglia", + "scale.aria": "Scala delle distanze con la soglia a {{cutoff}}. Più basso significa più simile. {{dots}}", + "scale.cutoff": "soglia {{value}}", + "scale.axis": "distanza tra impronte ({{print}}), più bassa significa più simili", + "scale.oneDot": "un punto per ogni registrazione ({{person}})", + "scale.onePair": "un punto per la coppia", + "ranks.aria": "Persone più vicine, dalla più vicina", + "ranks.within": "entro la soglia", + "ranks.far": "troppo lontana", + "cutoff.label": "Soglia", + "cutoff.default": "predefinita del modello", + "cutoff.reset": "Ripristina a {{value}}", + "cutoff.stricter": "Più severa", + "cutoff.looser": "Più larga", + "cutoff.explain": "Più severa significa meno corrispondenze sbagliate e più mancate. La risposta qui sopra si aggiorna mentre la sposti. Il server non viene interpellato di nuovo.", + "how.title": "Come leggere questo risultato.", + "how.body": "La distanza dice quanto sono lontane due impronte ({{print}}); 0 significa identiche. La soglia è la linea tra stessa persona e persone diverse. La parola Forte o Probabile dice quanto un risultato sta dentro la linea. Non è una probabilità.", + "why.title": "Perché potrebbe essere sbagliato", + "why.short": "La clip è breve: {{s}} s. Funziona meglio con almeno tre secondi di voce.", + "clip.mic": "microfono", + "clip.camera": "fotocamera", + "clip.record": "Registra", + "clip.takePhoto": "Scatta una foto", + "clip.chooseAudio": "Scegli un file", + "clip.choosePhoto": "Scegli una foto", + "clip.readAloud": "Leggi a voce alta, a ritmo normale", + "clip.remove": "Rimuovi", + "clip.blocked": "L'accesso a «{{thing}}» è bloccato. Consentilo nelle impostazioni del sito del browser e riprova, oppure scegli un file.", + "clip.insecure": "Il browser apre «{{thing}}» solo su https o su localhost. Ora sei su {{origin}}. Apri questa pagina da localhost o aggiungi TLS, oppure scegli un file.", + "clip.unsupported": "Questo browser non può aprire «{{thing}}». Scegli invece un file.", + "clip.missing": "Su questo dispositivo non è stato trovato «{{thing}}». Scegli invece un file.", + "clip.failed": "Non è stato possibile aprire «{{thing}}». Scegli invece un file.", + "clip.allowed": "Microfono consentito.", + "clip.capture": "Acquisisci", + "clip.cancel": "Annulla", + "clip.recording": "Registrazione in corso, {{s}} s", + "clip.micReady": "Microfono pronto", + "clip.stop": "Ferma", + "clip.start": "Avvia la registrazione", + "clip.recorded": "Registrazione", + "clip.photographed": "Foto", + "clip.uploaded": "File caricato", + "clip.goodLength": "durata adatta", + "clip.short": "breve", + "clip.checkLevel": "controlla il livello", + "clip.preview": "Anteprima", + "clip.photoAlt": "La foto che hai scelto", + "known.caption": "Persone salvate in questo browser, con lo stato della loro {{noun}}", + "known.name": "Nome", + "known.added": "Aggiunto", + "known.actions": "Azioni", + "known.best": "corrispondenza migliore", + "known.notOnServer": "non sul server", + "known.notInBrowser": "non nell'elenco di questo browser", + "known.removing": "rimozione", + "known.goneText": "L'ultima ricerca non ha restituito questa persona. Il server potrebbe essere stato riavviato, quindi l'{{print}} è persa, oppure è stata registrata con un altro modello.", + "known.extraText": "Il server ha restituito questa persona, ma questo browser non ne ha traccia.", + "known.noLabels": "nessuna etichetta", + "known.undo": "Annulla", + "known.reenrolSaved": "Registra di nuovo dalla copia salvata", + "known.enrolAgain": "Registra di nuovo", + "known.play": "Riproduci il campione salvato di {{name}}", + "known.forget": "Rimuovi {{name}}", + "known.note": "Questo elenco è conservato in questo browser. Il server non ha una chiamata per elencare, quindi non si può verificare finché non esegui una ricerca.", + "known.noteSearched": "Questo elenco è conservato in questo browser. L'ultima ricerca lo ha confrontato con il server; una persona che non è stata restituita è segnalata.", + "stored.title": "Cosa viene conservato e dove", + "enrol.close": "Chiudi", + "enrol.cancel": "Annulla", + "enrol.submit": "Registra", + "enrol.working": "Registrazione in corso", + "enrol.ready": "Pronto per registrare.", + "enrol.stillNeeded": "Manca ancora: {{list}}.", + "enrol.needModel": "un modello", + "enrol.needRecording": "una registrazione", + "enrol.needPhoto": "una foto", + "enrol.needName": "un nome", + "enrol.needTranscript": "una trascrizione", + "enrol.needPermission": "il permesso", + "enrol.recordingStep": "Registrazione", + "enrol.photoStep": "Foto", + "enrol.recordingLabel": "Registrazione", + "enrol.photoLabel": "Foto", + "enrol.whoStep": "Chi è", + "enrol.name": "Nome", + "enrol.namePlaceholder": "es. Marta Rossi", + "enrol.labels": "Etichette", + "enrol.labelsHint": "facoltative, una per riga come chiave: valore", + "enrol.asSpeech": "Conserva anche come voce di sintesi", + "enrol.asSpeechHint": "La sintesi vocale potrà poi parlare con questa voce. Serve la trascrizione esatta.", + "enrol.transcript": "Trascrizione esatta", + "enrol.permissionStep": "Permesso", + "enrol.agreed": "Questa persona ha acconsentito", + "enrol.keep": "Conserva una copia in questo browser", + "toast.enrolled": "{{name}} registrato", + "toast.enrolledBoth": "{{name}} registrato e voce di sintesi salvata", + "toast.tooBig": "La copia era troppo grande per la memoria del browser, quindi non è stata conservata.", + "toast.speechFailed": "Registrazione riuscita, ma la voce di sintesi non è stata salvata: {{message}}", + "toast.removing": "Rimozione di {{name}}…", + "toast.removed": "{{name}} rimosso", + "toast.alreadyGone": "{{name}} non c'era già più sul server.", + "error.retry": "Riprova", + "error.traces": "Apri le tracce", + "needed.badge": "Non attivo su questo server", + "needed.backend": "backend {{backend}}", + "needed.install": "Installa", + "needed.installing": "Installazione", + "needed.started": "Installazione di {{name}}. L'avanzamento è in Operazioni.", + "needed.loadFailed": "Non è stato possibile elencare i modelli.", + "needed.noneListed": "Nelle gallerie non c'è nessun modello corrispondente.", + "needed.browse": "Sfoglia tutti i modelli", + "needed.askAdmin": "Chiedi a un amministratore di installarne uno.", + "needed.whatTitle": "Cosa lo attiva", + "off.badge": "Non attivo per il tuo account", + "off.voice.title": "Il riconoscimento vocale è disattivato per il tuo account", + "off.voice.text": "Un amministratore non ti ha consentito di usare il riconoscimento vocale.", + "off.voice.what": "Un amministratore può attivare il permesso di riconoscimento vocale per il tuo utente in Utenti e chiavi.", + "off.face.title": "Il riconoscimento facciale è disattivato per il tuo account", + "off.face.text": "Un amministratore non ti ha consentito di usare il riconoscimento facciale.", + "off.face.what": "Un amministratore può attivare il permesso di riconoscimento facciale per il tuo utente in Utenti e chiavi." +} diff --git a/addons/face/ui/src/i18n/face.ko.json b/addons/face/ui/src/i18n/face.ko.json new file mode 100644 index 0000000000..dfebdb34a3 --- /dev/null +++ b/addons/face/ui/src/i18n/face.ko.json @@ -0,0 +1,187 @@ +{ + "faces.title": "얼굴", + "faces.lede": "Find out whose face is in a photo, or whether two photos show the same person. Photos go only to the model running on this server.", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "진행 중", + "face.person": "person", + "face.print": "faceprint", + "face.whoTab": "Who is this", + "face.whoLede": "Matches a photo against the people you enrolled.", + "face.sameLede": "Two photos, one question. Nobody has to be enrolled.", + "face.whoGo": "Find who this is", + "face.working": "검색 중", + "face.whoNote": "Searches the people enrolled on this server. The photo you test with is not stored.", + "face.sameNote": "The answer comes from one faceprint per photo. Nothing is stored.", + "face.workingText": "Comparing the photo with the faceprints the server holds.", + "face.probably": "Probably {{name}}", + "face.matchText": "{{name}}'s faceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "face.nobody": "Nobody enrolled looks like this", + "face.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new person, or a photo that is blurred, turned away or in poor light.", + "face.noneEnrolled": "Nobody is enrolled to compare with", + "face.noneEnrolledText": "Enrol a person first, then look up a photo against them.", + "face.noneReturned": "The server returned no people", + "face.noneReturnedText": "It holds no faceprints made with this model. If the server restarted, enrolled faces are gone: enrol them again.", + "face.enrolThis": "Enrol this face", + "face.same": "Probably the same person", + "face.different": "Probably different people", + "face.knownTitle": "Known people", + "face.enrol": "Enrol a person", + "face.enrolTitle": "Enrol a person", + "face.enrolIntro": "The server keeps a faceprint and a name. It does not keep the photo.", + "face.emptyTitle": "Nobody enrolled in this browser yet", + "face.emptyText": "Enrol a person with one clear photo, then test a photo against them.", + "face.agreedHint": "I have their permission, or another lawful basis, to store a faceprint of them.", + "face.keepHint": "Lets you see the photo and enrol again after a server restart. Off by default. It is stored in this browser only.", + "face.antiSpoof": "Check for photos of photos", + "face.antiSpoofHint": "Needs a model that supports it.", + "face.real": "Real", + "face.spoof": "Spoof", + "face.found": "Face in photo {{n}}", + "face.neededTitle": "Faces need a face model", + "face.neededText": "Face recognition turns a photo into a faceprint and compares faceprints. It runs on a face-recognition model, and none is installed on this server.", + "face.neededWhat": "Install a model that supports face recognition. Once one is installed, this page turns on by itself. Users also need the Face recognition permission.", + "face.moreTitle": "More tools", + "face.moreHint": "detect faces, guess traits, get the raw faceprint", + "face.why.0": "The server matches one face per photo. A group photo is matched on a single face.", + "face.why.1": "Light, angle, glasses and blur change a face a lot.", + "face.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "face.stored.0.where": "On the server", + "face.stored.0.what": "A {{print}} (numbers that describe the face, not the picture), a name and your labels. They are kept in memory and are lost when the server restarts. The photo is not kept.", + "face.stored.1.where": "In this browser", + "face.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the photo only if you tick Keep a copy when enrolling.", + "face.stored.2.where": "Photos you test with", + "face.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Record", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "제거", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "취소", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "중지", + "clip.start": "Start recording", + "clip.recorded": "Recording", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "미리보기", + "clip.photoAlt": "The photo you chose", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "이름", + "known.added": "추가됨", + "known.actions": "작업", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "실행 취소", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "{{name}} 제거", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "stored.title": "What is stored, and where", + "enrol.close": "닫기", + "enrol.cancel": "취소", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Recording", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Recording", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "이름", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Exact transcript", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "다시 시도", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "설치", + "needed.installing": "설치 중", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Browse all models", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "off.face.title": "Face recognition is off for your account", + "off.face.text": "An administrator has not allowed you to use face recognition.", + "off.face.what": "An administrator can turn on the Face recognition permission for your user in Users and keys." +} diff --git a/addons/face/ui/src/i18n/face.pt-BR.json b/addons/face/ui/src/i18n/face.pt-BR.json new file mode 100644 index 0000000000..55a034be41 --- /dev/null +++ b/addons/face/ui/src/i18n/face.pt-BR.json @@ -0,0 +1,187 @@ +{ + "faces.title": "Rostos", + "faces.lede": "Find out whose face is in a photo, or whether two photos show the same person. Photos go only to the model running on this server.", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "Em andamento", + "face.person": "person", + "face.print": "faceprint", + "face.whoTab": "Who is this", + "face.whoLede": "Matches a photo against the people you enrolled.", + "face.sameLede": "Two photos, one question. Nobody has to be enrolled.", + "face.whoGo": "Find who this is", + "face.working": "Pesquisando", + "face.whoNote": "Searches the people enrolled on this server. The photo you test with is not stored.", + "face.sameNote": "The answer comes from one faceprint per photo. Nothing is stored.", + "face.workingText": "Comparing the photo with the faceprints the server holds.", + "face.probably": "Probably {{name}}", + "face.matchText": "{{name}}'s faceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "face.nobody": "Nobody enrolled looks like this", + "face.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new person, or a photo that is blurred, turned away or in poor light.", + "face.noneEnrolled": "Nobody is enrolled to compare with", + "face.noneEnrolledText": "Enrol a person first, then look up a photo against them.", + "face.noneReturned": "The server returned no people", + "face.noneReturnedText": "It holds no faceprints made with this model. If the server restarted, enrolled faces are gone: enrol them again.", + "face.enrolThis": "Enrol this face", + "face.same": "Probably the same person", + "face.different": "Probably different people", + "face.knownTitle": "Known people", + "face.enrol": "Enrol a person", + "face.enrolTitle": "Enrol a person", + "face.enrolIntro": "The server keeps a faceprint and a name. It does not keep the photo.", + "face.emptyTitle": "Nobody enrolled in this browser yet", + "face.emptyText": "Enrol a person with one clear photo, then test a photo against them.", + "face.agreedHint": "I have their permission, or another lawful basis, to store a faceprint of them.", + "face.keepHint": "Lets you see the photo and enrol again after a server restart. Off by default. It is stored in this browser only.", + "face.antiSpoof": "Check for photos of photos", + "face.antiSpoofHint": "Needs a model that supports it.", + "face.real": "Real", + "face.spoof": "Spoof", + "face.found": "Face in photo {{n}}", + "face.neededTitle": "Faces need a face model", + "face.neededText": "Face recognition turns a photo into a faceprint and compares faceprints. It runs on a face-recognition model, and none is installed on this server.", + "face.neededWhat": "Install a model that supports face recognition. Once one is installed, this page turns on by itself. Users also need the Face recognition permission.", + "face.moreTitle": "More tools", + "face.moreHint": "detect faces, guess traits, get the raw faceprint", + "face.why.0": "The server matches one face per photo. A group photo is matched on a single face.", + "face.why.1": "Light, angle, glasses and blur change a face a lot.", + "face.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "face.stored.0.where": "On the server", + "face.stored.0.what": "A {{print}} (numbers that describe the face, not the picture), a name and your labels. They are kept in memory and are lost when the server restarts. The photo is not kept.", + "face.stored.1.where": "In this browser", + "face.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the photo only if you tick Keep a copy when enrolling.", + "face.stored.2.where": "Photos you test with", + "face.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Gravar", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "Remover", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "Cancelar", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "Parar", + "clip.start": "Iniciar gravação", + "clip.recorded": "Recording", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "Pré-visualização", + "clip.photoAlt": "The photo you chose", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "Nome", + "known.added": "Adicionado", + "known.actions": "Ações", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "Desfazer", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "Remover {{name}}", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "stored.title": "What is stored, and where", + "enrol.close": "Fechar", + "enrol.cancel": "Cancelar", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Recording", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Recording", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "Nome", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Transcrição exata", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "Tentar novamente", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "Instalar", + "needed.installing": "Instalando", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Explorar todos os modelos", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "off.face.title": "Face recognition is off for your account", + "off.face.text": "An administrator has not allowed you to use face recognition.", + "off.face.what": "An administrator can turn on the Face recognition permission for your user in Users and keys." +} diff --git a/addons/face/ui/src/i18n/face.zh-CN.json b/addons/face/ui/src/i18n/face.zh-CN.json new file mode 100644 index 0000000000..91f630d010 --- /dev/null +++ b/addons/face/ui/src/i18n/face.zh-CN.json @@ -0,0 +1,187 @@ +{ + "faces.title": "人脸", + "faces.lede": "Find out whose face is in a photo, or whether two photos show the same person. Photos go only to the model running on this server.", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "进行中", + "face.person": "person", + "face.print": "faceprint", + "face.whoTab": "Who is this", + "face.whoLede": "Matches a photo against the people you enrolled.", + "face.sameLede": "Two photos, one question. Nobody has to be enrolled.", + "face.whoGo": "Find who this is", + "face.working": "正在搜索", + "face.whoNote": "Searches the people enrolled on this server. The photo you test with is not stored.", + "face.sameNote": "The answer comes from one faceprint per photo. Nothing is stored.", + "face.workingText": "Comparing the photo with the faceprints the server holds.", + "face.probably": "Probably {{name}}", + "face.matchText": "{{name}}'s faceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "face.nobody": "Nobody enrolled looks like this", + "face.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new person, or a photo that is blurred, turned away or in poor light.", + "face.noneEnrolled": "Nobody is enrolled to compare with", + "face.noneEnrolledText": "Enrol a person first, then look up a photo against them.", + "face.noneReturned": "The server returned no people", + "face.noneReturnedText": "It holds no faceprints made with this model. If the server restarted, enrolled faces are gone: enrol them again.", + "face.enrolThis": "Enrol this face", + "face.same": "Probably the same person", + "face.different": "Probably different people", + "face.knownTitle": "Known people", + "face.enrol": "Enrol a person", + "face.enrolTitle": "Enrol a person", + "face.enrolIntro": "The server keeps a faceprint and a name. It does not keep the photo.", + "face.emptyTitle": "Nobody enrolled in this browser yet", + "face.emptyText": "Enrol a person with one clear photo, then test a photo against them.", + "face.agreedHint": "I have their permission, or another lawful basis, to store a faceprint of them.", + "face.keepHint": "Lets you see the photo and enrol again after a server restart. Off by default. It is stored in this browser only.", + "face.antiSpoof": "Check for photos of photos", + "face.antiSpoofHint": "Needs a model that supports it.", + "face.real": "Real", + "face.spoof": "Spoof", + "face.found": "Face in photo {{n}}", + "face.neededTitle": "Faces need a face model", + "face.neededText": "Face recognition turns a photo into a faceprint and compares faceprints. It runs on a face-recognition model, and none is installed on this server.", + "face.neededWhat": "Install a model that supports face recognition. Once one is installed, this page turns on by itself. Users also need the Face recognition permission.", + "face.moreTitle": "More tools", + "face.moreHint": "detect faces, guess traits, get the raw faceprint", + "face.why.0": "The server matches one face per photo. A group photo is matched on a single face.", + "face.why.1": "Light, angle, glasses and blur change a face a lot.", + "face.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "face.stored.0.where": "On the server", + "face.stored.0.what": "A {{print}} (numbers that describe the face, not the picture), a name and your labels. They are kept in memory and are lost when the server restarts. The photo is not kept.", + "face.stored.1.where": "In this browser", + "face.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the photo only if you tick Keep a copy when enrolling.", + "face.stored.2.where": "Photos you test with", + "face.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Record", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "移除", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "取消", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "停止", + "clip.start": "Start recording", + "clip.recorded": "Recording", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "预览", + "clip.photoAlt": "The photo you chose", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "名称", + "known.added": "添加时间", + "known.actions": "操作", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "撤销", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "移除 {{name}}", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "stored.title": "What is stored, and where", + "enrol.close": "关闭", + "enrol.cancel": "取消", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Recording", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Recording", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "名称", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Exact transcript", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "重试", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "安装", + "needed.installing": "正在安装", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Browse all models", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "off.face.title": "Face recognition is off for your account", + "off.face.text": "An administrator has not allowed you to use face recognition.", + "off.face.what": "An administrator can turn on the Face recognition permission for your user in Users and keys." +} diff --git a/addons/face/ui/src/i18n/index.js b/addons/face/ui/src/i18n/index.js new file mode 100644 index 0000000000..4254058e9c --- /dev/null +++ b/addons/face/ui/src/i18n/index.js @@ -0,0 +1,16 @@ +// The words of the face pages, one JSON bundle per language of the web UI. +// English is the source and the fallback of every key. Italian is a draft +// that wants a native review; a language whose bundle repeats the English +// text of a key has no translation for it yet. addons/internal/i18ncheck +// checks that every bundle has the keys and placeholders of English. +import en from './face.en.json' +import it from './face.it.json' +import de from './face.de.json' +import es from './face.es.json' +import fr from './face.fr.json' +import id from './face.id.json' +import ko from './face.ko.json' +import pt_BR from './face.pt-BR.json' +import zh_CN from './face.zh-CN.json' + +export const locales = { en, it, de, es, fr, id, ko, 'pt-BR': pt_BR, 'zh-CN': zh_CN } diff --git a/addons/face/ui/test/recognition.test.mjs b/addons/face/ui/test/recognition.test.mjs index 701e8d6b19..12595f7dd7 100644 --- a/addons/face/ui/test/recognition.test.mjs +++ b/addons/face/ui/test/recognition.test.mjs @@ -69,7 +69,7 @@ test('recognition: the bundle has the face canvas, the face strings and no host test('recognition: every translation key the sources read is in the Face bundle', async () => { const { readFileSync, readdirSync, statSync } = await import('node:fs') - const { default: en } = await import('../src/i18n/en.js') + const en = JSON.parse(readFileSync(join(here, '../src/i18n/face.en.json'), 'utf8')) const roots = [join(here, '../src'), join(here, '../../../internal/identity-ui/src')] const files = [] const walk = (d) => { for (const f of readdirSync(d)) { const p = join(d, f); if (statSync(p).isDirectory()) walk(p); else if (/\.jsx?$/.test(p) && !p.includes('i18n')) files.push(p) } } diff --git a/addons/fine-tuning/ui/src/ctx.jsx b/addons/fine-tuning/ui/src/ctx.jsx index e559efb73b..fb5ca9f090 100644 --- a/addons/fine-tuning/ui/src/ctx.jsx +++ b/addons/fine-tuning/ui/src/ctx.jsx @@ -1,4 +1,4 @@ -import en from './i18n/en.js' +import { locales } from './i18n/index.js' import { modelFrom as pickModel } from './lib/model.js' // The one object every factory of the fine-tuning add-on receives. It binds the @@ -10,7 +10,9 @@ export function createContext(sdk) { const { React } = sdk const h = React.createElement const Fragment = React.Fragment - sdk.locale.define('en', Object.fromEntries(Object.entries(en).filter(([, v]) => typeof v === 'string'))) + // Every language of the page; a key a language lacks reads in English. + for (const [language, bundle] of Object.entries(locales)) sdk.locale.define(language, bundle) + const en = locales.en const t = (key, vars) => sdk.locale.t(key, key, vars) // The text of a key, or the fallback when the bundle has none. diff --git a/addons/fine-tuning/ui/src/i18n/fine-tuning.de.json b/addons/fine-tuning/ui/src/i18n/fine-tuning.de.json new file mode 100644 index 0000000000..1da57925cc --- /dev/null +++ b/addons/fine-tuning/ui/src/i18n/fine-tuning.de.json @@ -0,0 +1,295 @@ +{ + "steps.label": "Fortschritt", + "steps.setup": "Einrichten", + "steps.check": "Prüfen", + "steps.run": "Ausführen", + "steps.result": "Ergebnis", + "run.stop": "Stoppen", + "run.connecting": "Verbindung zum Job wird hergestellt. Warten auf das erste Fortschrittsereignis.", + "run.progress": "Fortschritt", + "run.stages": "Phasen", + "run.step": "Schritt", + "run.of": "von", + "run.epoch": "Epoche", + "run.tokens": "Token/s", + "run.eta": "noch etwa {{time}}", + "run.etaSeconds": "noch etwa {{seconds}} s", + "checks.heading": "Vor dem Start prüfen", + "checks.live": "Wird aktualisiert, während Sie die Einrichtung ändern.", + "checks.meterLabel": "{{used}} Prozent belegt, {{free}} von {{total}} frei", + "checks.openBackends": "Backends öffnen", + "checks.barOk": "Nichts blockiert den Start.", + "checks.barWarn_one": "{{count}} Warnung. Sie können trotzdem starten.", + "checks.barWarn_other": "{{count}} Warnungen. Sie können trotzdem starten.", + "checks.title.model": "Modell", + "checks.title.data": "Datensatz", + "checks.title.backend": "Backend", + "checks.title.memory": "Speicher", + "checks.title.disk": "Festplatte", + "checks.title.reward": "Belohnungsfunktionen", + "checks.title.resume": "Fortsetzen", + "checks.title.access": "Modellzugriff", + "checks.title.type": "Quantisierungstyp", + "checks.title.source": "Quelle", + "checks.title.name": "Name", + "checks.fineTune.modelOk": "Training von {{model}}.", + "checks.fineTune.modelMissing": "Geben Sie das Modell ein, das trainiert werden soll.", + "checks.fineTune.dataOk": "Die Daten kommen von {{source}}.", + "checks.fineTune.dataMissing": "Fügen Sie einen Datensatz hinzu: eine Hugging-Face-ID oder eine hochgeladene Datei.", + "checks.fineTune.backendOk": "{{name}} ist installiert.", + "checks.fineTune.backendMissing": "Kein Backend für Feinabstimmung ist installiert, der Job kann daher nicht starten.", + "checks.fineTune.backendOther": "{{name}} gehört nicht zu den installierten Backends für Feinabstimmung ({{list}}).", + "checks.fineTune.backendUnknown": "Die installierten Backends konnten nicht gelesen werden.", + "checks.fineTune.memoryGpu": "{{name}}: {{free}} von {{total}} frei. Batch-Größe {{batch}}, Sequenzlänge {{seq}}, Gradientenakkumulation {{accum}}. Wie viel der Job braucht, hängt vom Modell und diesen Einstellungen ab. LocalAI schätzt es vor dem Start des Jobs nicht.", + "checks.fineTune.memoryCluster": "{{total}} auf dem besten Knoten ({{node}}). Batch-Größe {{batch}}, Sequenzlänge {{seq}}. Wie viel der Job braucht, hängt vom Modell und diesen Einstellungen ab.", + "checks.fineTune.memoryCpu": "Keine GPU gefunden. Das Training läuft auf der CPU, und {{free}} von {{total}} RAM sind frei. Rechnen Sie mit Langsamkeit. Batch-Größe {{batch}}, Sequenzlänge {{seq}}.", + "checks.fineTune.memoryUnknown": "Dieses Konto kann den Speicher des Rechners nicht lesen. Batch-Größe {{batch}}, Sequenzlänge {{seq}}. Wie viel der Job braucht, hängt vom Modell und diesen Einstellungen ab.", + "checks.fineTune.disk": "{{free}} frei auf dem Modell-Speicher. Downloads und Checkpoints brauchen Platz.", + "checks.fineTune.diskLow": "Nur {{free}} frei auf dem Modell-Speicher. Downloads und Checkpoints brauchen Platz.", + "checks.fineTune.rewardMissing": "GRPO braucht mindestens eine Belohnungsfunktion. Fügen Sie unter Weitere Optionen eine hinzu.", + "checks.fineTune.resume": "Das Training wird ab {{path}} fortgesetzt.", + "checks.fineTune.accessNone": "Kein Token gesetzt. Ein geschütztes Modell braucht ein Hugging-Face-Token unter Weitere Optionen.", + "checks.fineTune.accessSet": "Ein Hugging-Face-Token ist gesetzt.", + "checks.quantize.modelOk": "Quantisierung von {{model}}.", + "checks.quantize.modelMissing": "Geben Sie das Modell ein, das quantisiert werden soll.", + "checks.quantize.typeMissing": "Geben Sie den eigenen Quantisierungstyp ein.", + "checks.quantize.backendOk": "{{name}} ist installiert.", + "checks.quantize.backendMissing": "Kein Quantisierungs-Backend ist installiert, der Job kann daher nicht starten.", + "checks.quantize.backendOther": "{{name}} gehört nicht zu den installierten Quantisierungs-Backends ({{list}}).", + "checks.quantize.backendUnknown": "Die installierten Backends konnten nicht gelesen werden.", + "checks.quantize.memory": "{{free}} von {{total}} RAM sind frei. Die Umwandlung läuft auf der CPU.", + "checks.quantize.disk": "{{free}} frei auf dem Modell-Speicher. Download, umgewandelte Datei und Ergebnis werden auf die Festplatte geschrieben.", + "checks.quantize.diskLow": "Nur {{free}} frei auf dem Modell-Speicher. Download, umgewandelte Datei und Ergebnis werden auf die Festplatte geschrieben.", + "checks.quantize.accessNone": "Kein Token gesetzt. Ein geschütztes Modell braucht ein Hugging-Face-Token unter Weitere Optionen.", + "checks.quantize.accessSet": "Ein Hugging-Face-Token ist gesetzt.", + "checks.import.sourceOk_huggingface": "Erkannt als Hugging-Face-Repository. LocalAI prüft seine Dateien beim Import.", + "checks.import.sourceOk_url": "URL-Format erkannt. Der Inhalt wurde nicht geprüft.", + "checks.import.sourceOk_config": "Erkannt als Konfigurationsdatei eines Modells. Sie wird so gelesen und verwendet, wie sie ist.", + "checks.import.sourceOk_oci": "Erkannt als OCI-Image-Referenz.", + "checks.import.sourceOk_ollama": "Erkannt als Ollama-Modellreferenz.", + "checks.import.sourceOk_local": "Erkannt als Pfad auf dem Rechner, auf dem LocalAI läuft.", + "checks.import.sourceOk": "Die Quelle ist erkannt.", + "checks.import.sourceUnknown": "Das sieht nach keinem Format aus, das LocalAI kennt. Es wird trotzdem versucht und kann fehlschlagen.", + "checks.import.backendDownload": "{{name}} ist noch nicht installiert. LocalAI lädt es zuerst herunter.", + "checks.import.backendChosen": "{{name}} ist ausgewählt und wird installiert.", + "checks.import.backendAuto": "LocalAI wählt das Backend anhand der Dateien der Quelle, sobald der Import beginnt.", + "checks.import.nameTaken": "Ein Modell namens {{name}} ist bereits installiert.", + "checks.import.nameFree": "{{name}} ist nicht in Verwendung.", + "checks.import.nameAuto": "Das Modell erhält den Namen der Quelle.", + "checks.import.disk": "{{free}} frei auf dem Modell-Speicher. Die Download-Größe ist bekannt, sobald der Import beginnt.", + "checks.import.memory_gpu": "{{free}} GPU-Speicher sind jetzt frei. Was dieses Modell braucht, ist bekannt, sobald der Import beginnt.", + "checks.import.memory_ram": "{{free}} RAM sind jetzt frei. Was dieses Modell braucht, ist bekannt, sobald der Import beginnt.", + "checks.import.memory_cluster": "{{free}} sind auf dem besten Knoten verfügbar. Was dieses Modell braucht, ist bekannt, sobald der Import beginnt.", + "checks.import.memory": "{{free}} Speicher sind jetzt frei. Was dieses Modell braucht, ist bekannt, sobald der Import beginnt.", + "form.key": "Schlüssel", + "form.value": "Wert", + "form.addOption": "Option hinzufügen", + "form.copy": "In die Zwischenablage kopieren", + "form.more": "Weitere Optionen", + "form.moreHint": "Methode, Backend, Adapter, Optimierer, Auswertung, Belohnungsfunktionen", + "form.baseModel": "Basismodell", + "form.model": "Modell", + "form.modelHint": "Eine Hugging-Face-ID oder ein Pfad auf diesem Host.", + "form.data": "Ihre Daten", + "form.dataset": "Quelle des Datensatzes", + "form.datasetHint": "Eine Hugging-Face-Datensatz-ID, oder laden Sie eine Datei hoch.", + "form.split": "Aufteilung", + "form.upload": "Stattdessen eine Datei hochladen", + "form.uploadHint": ".json, .jsonl oder .csv", + "form.removeFile": "Datei entfernen", + "form.train": "Was trainiert werden soll", + "form.kind": "Art des Trainings", + "form.kinds.adapter.title": "Adapter", + "form.kinds.adapter.text": "Trainiert einen kleinen Adapter und lässt die Basisgewichte unverändert. Die übliche Wahl, und sie braucht wenig Speicher.", + "form.kinds.full.title": "Vollständiges Modell", + "form.kinds.full.text": "Ändert jedes Gewicht. Es braucht ein Vielfaches des Speichers eines Adapters.", + "form.epochs": "Epochen", + "form.batch": "Batch-Größe", + "form.lr": "Lernrate", + "form.backend": "Backend", + "form.method": "Trainingsmethode", + "form.adapterKind": "Adaptertyp", + "form.rank": "Rang", + "form.alpha": "Alpha", + "form.dropout": "Dropout", + "form.gradAccum": "Schritte der Gradientenakkumulation", + "form.warmup": "Warmup-Schritte", + "form.seqLen": "Maximale Sequenzlänge", + "form.optimizer": "Optimierer", + "form.maxSteps": "Maximale Schritte", + "form.maxStepsHint": "0 für automatisch.", + "form.saveSteps": "Speicherschritte", + "form.saveLimit": "Gesamtlimit für Speicherungen", + "form.saveLimitHint": "0 behält jeden Checkpoint.", + "form.weightDecay": "Gewichtsabnahme", + "form.seed": "Seed", + "form.seedHint": "0 wählt einen zufälligen Seed.", + "form.precision": "Gemischte Genauigkeit", + "form.auto": "Automatisch", + "form.targetModules": "Zielmodule", + "form.targetModulesHint": "Durch Kommas getrennt. Leer lassen für den Standard des Backends.", + "form.gradCheckpointing": "Gradient Checkpointing (langsamer, braucht weniger Speicher)", + "form.token": "Hugging-Face-Token", + "form.tokenHint": "Nur für geschützte Modelle nötig.", + "form.evalEnable": "Während des Trainings auswerten", + "form.evalStrategy": "Auswertungsstrategie", + "form.evalSteps_": "Schritte", + "form.evalEpoch": "Epoche", + "form.evalSteps": "Auswertungsschritte", + "form.evalStepsHint": "0 entspricht den Speicherschritten.", + "form.evalSplit": "Aufteilung für die Auswertung", + "form.evalDataset": "Datensatz für die Auswertung", + "form.evalDatasetPlaceholder": "Ein eigener Datensatz", + "form.evalRatio": "Verhältnis der automatischen Aufteilung", + "form.rewards": "Belohnungsfunktionen (GRPO)", + "form.rewardsHint": "GRPO braucht mindestens eine Belohnungsfunktion. Wählen Sie eingebaute aus oder fügen Sie eigene hinzu.", + "form.resume": "Vom Checkpoint fortsetzen", + "form.clearCheckpoint": "Checkpoint löschen", + "form.extra": "Zusätzliche Optionen", + "form.extraHint": "Schlüssel-Wert-Paare, die vom Backend abhängen.", + "form.summaryFull": "vollständiges Modell", + "form.summary_lora": "LoRA-Adapter", + "form.summary_loha": "LoHa-Adapter", + "form.summary_lokr": "LoKr-Adapter", + "form.recipe": "Trainiert einen {{kind}} von {{model}} mit {{method}}, {{epochs}} Epochen über {{data}}.", + "form.recipeEmpty": "Wählen Sie ein Modell und einen Datensatz, um zu sehen, was trainiert wird.", + "rewards.format_reward": "Prüft das Format ... gefolgt von der Antwort.", + "rewards.reasoning_accuracy_reward": "Vergleicht den Inhalt von mit der Antwortspalte des Datensatzes.", + "rewards.length_reward": "Bewertet, wie nah die Antwort an einer Ziellänge liegt.", + "rewards.xml_tag_reward": "Bewertet korrekt geöffnete und geschlossene XML-Tags.", + "rewards.no_repetition_reward": "Bestraft wiederholte n-Gramme.", + "rewards.code_execution_reward": "Prüft, ob Python-Codeblöcke gültige Syntax haben.", + "rewards.param_target_length": "Ziellänge", + "rewards.fnName": "Funktionsname", + "rewards.fnBody": "Funktionskörper", + "rewards.fnHint": "Erhält completions und kwargs und muss eine Liste von Gleitkommazahlen zurückgeben. re, math, json und string sind verfügbar.", + "rewards.add": "Hinzufügen", + "rewards.cancel": "Abbrechen", + "rewards.custom": "Eigene Belohnungsfunktion hinzufügen", + "fineTune.title": "Feinabstimmung", + "fineTune.lede": "Trainieren Sie einen Adapter mit Ihren eigenen Daten und exportieren Sie ihn als Modell, mit dem Sie chatten können.", + "fineTune.importConfig": "Konfiguration importieren", + "fineTune.exportConfig": "Konfiguration exportieren", + "fineTune.newJob": "Neuer Job", + "fineTune.badConfig": "Diese Datei konnte nicht gelesen werden. Verwenden Sie eine JSON-Konfiguration, die auf dieser Seite exportiert wurde.", + "fineTune.start": "Feinabstimmung starten", + "fineTune.starting": "Wird gestartet...", + "fineTune.resume": "Training fortsetzen", + "fineTune.resuming": "Wird vom Checkpoint fortgesetzt:", + "fineTune.clear": "Leeren", + "fineTune.barNote": "Nichts wird geändert, bis Sie starten.", + "fineTune.jobMeta": "{{kind}} {{method}} auf {{backend}}, gestartet {{started}}", + "fineTune.failed": "Training fehlgeschlagen", + "fineTune.failedAt": "Training bei Schritt {{step}} von {{total}} gestoppt", + "fineTune.noMessage": "Das Backend hat keine Meldung ausgegeben.", + "fineTune.finished": "Training abgeschlossen.", + "fineTune.finishedLoss": "Training abgeschlossen. Der letzte Loss war {{loss}}.", + "fineTune.stopped": "Der Job wurde gestoppt. Ein Checkpoint bleibt erhalten, wenn Sie das gewählt haben.", + "fineTune.tryTitle": "Was als Nächstes zu tun ist", + "fineTune.tryText": "Der Server meldet, dass der Speicher nicht mehr reicht. Jede Änderung unten wird auf eine Kopie der Einrichtung angewendet, und nichts startet, bis Sie Start drücken.", + "fineTune.tryPlain": "Öffnen Sie die Einrichtung erneut, ändern Sie, worauf die Meldung hinweist, und starten Sie einen neuen Job.", + "fineTune.fixBatch": "Batch-Größe auf 1 senken", + "fineTune.fixCheckpointing": "Gradient Checkpointing einschalten (langsamer, braucht weniger Speicher)", + "fineTune.retryChanges": "Einrichtung mit diesen Änderungen öffnen", + "fineTune.reuse": "Einrichtung erneut öffnen", + "fineTune.resumeFrom": "Ab Schritt {{step}} fortsetzen", + "fineTune.status.queued": "In der Warteschlange", + "fineTune.status.loading_model": "Modell wird geladen", + "fineTune.status.loading_dataset": "Datensatz wird geladen", + "fineTune.status.training": "Training", + "fineTune.status.saving": "Speichern", + "fineTune.status.completed": "Abgeschlossen", + "fineTune.status.failed": "Fehlgeschlagen", + "fineTune.status.stopped": "Gestoppt", + "stop.title": "Training stoppen?", + "stop.text": "Ein jetzt gespeicherter Checkpoint erlaubt es, das bisher Gelernte fortzusetzen oder zu exportieren.", + "stop.keep": "Stoppen und einen Checkpoint behalten", + "stop.discard": "Ohne Checkpoint stoppen", + "stop.cancel": "Training fortsetzen", + "stop.cancelDelete": "Abbrechen", + "chart.loss": "Loss", + "chart.learning_rate": "Lernrate", + "chart.grad_norm": "Gradientennorm", + "chart.metric": "Metrik", + "chart.step": "Schritt", + "chart.eval": "Auswertungs-Loss", + "chart.waiting": "Warten auf weitere Schritte.", + "chart.window": "Schritte {{from}} bis {{to}} von {{total}}", + "chart.windowEval": "Schritte {{from}} bis {{to}} von {{total}}. Die hohlen Punkte sind der Auswertungs-Loss.", + "chart.aria": "{{metric}} über die Trainingsschritte. Der letzte Wert ist {{last}} bei Schritt {{step}}.", + "chart.readout": "Schritt {{step}}: {{value}}", + "chart.dataTitle": "Die Zahlen anzeigen", + "chart.emptyText": "Das Diagramm wird gezeichnet, sobald der Job zwei Schritte gemeldet hat.", + "log.title": "Protokoll", + "log.note": "Was der Job seit dem Öffnen dieser Seite gemeldet hat.", + "log.only": "Nur Warnungen und Fehler", + "log.copy": "Kopieren", + "log.copied": "Kopiert", + "log.download": "Herunterladen", + "log.empty": "Noch nichts gemeldet.", + "log.noneFiltered": "Keine Warnungen oder Fehler.", + "checkpoints.title": "Checkpoints", + "checkpoints.step": "Schritt", + "checkpoints.epoch": "Epoche", + "checkpoints.loss": "Loss", + "checkpoints.path": "Pfad", + "checkpoints.actions": "Aktionen", + "checkpoints.resume": "Fortsetzen", + "checkpoints.resumeTitle": "Training ab diesem Checkpoint fortsetzen", + "checkpoints.export": "Exportieren", + "checkpoints.exportTitle": "Diesen Checkpoint exportieren", + "export.blocked": "Der Export ist auf diesem Server ausgeschaltet, weil er den Modellordner nicht mit dem Add-on-Host teilt.", + "export.title": "Als Modell exportieren", + "export.lede": "Beim Exportieren wird das Ergebnis in ein Modell umgewandelt, das LocalAI laden kann.", + "export.checkpoint": "Checkpoint", + "export.final": "Endmodell (Ausgabeverzeichnis)", + "export.checkpointOption": "Schritt {{step}} (Loss {{loss}})", + "export.format": "Exportformat", + "export.lora": "LoRA-Adapter", + "export.merged16": "Zusammengeführt (16 Bit)", + "export.merged4": "Zusammengeführt (4 Bit)", + "export.quant": "Quantisierung", + "export.name": "Modellname", + "export.nameHint": "Leer lassen, um einen Namen aus dem Job zu erzeugen.", + "export.go": "Exportieren", + "export.exporting": "Wird exportiert...", + "export.working": "Export läuft...", + "export.done": "Modell exportiert und als \"{{name}}\" registriert", + "export.failed": "Export fehlgeschlagen: {{message}}", + "export.unknown": "unbekannter Fehler", + "export.nextTitle": "{{name}} ist jetzt ein Modell", + "export.nextText": "Es ist bei LocalAI registriert. Öffnen Sie einen Chat damit oder finden Sie es unter Modelle.", + "export.chat": "Mit {{name}} chatten", + "export.models": "Unter Modelle öffnen", + "export.archive": "Archiv herunterladen", + "jobs.title": "Frühere Jobs", + "jobs.count_one": "{{count}} auf diesem Server gespeichert", + "jobs.count_other": "{{count}} auf diesem Server gespeichert", + "jobs.loading": "Jobs werden geladen", + "jobs.emptyTitle": "Noch keine Feinabstimmungs-Jobs", + "jobs.emptyText": "Richten Sie oben einen ein. Er erscheint hier mit seinem Status, und Sie können seine Einrichtung später wiederverwenden.", + "jobs.model": "Modell", + "jobs.kind": "Art", + "jobs.status": "Status", + "jobs.started": "Gestartet", + "jobs.actions": "Aktionen", + "jobs.open": "Diesen Job öffnen", + "jobs.reuse": "Wiederverwenden", + "jobs.reuseTitle": "Die Einrichtung dieses Jobs für einen neuen Job verwenden", + "jobs.delete": "Löschen", + "jobs.deleteTitle": "Diesen Job und seine Daten löschen", + "jobs.deleteDialogTitle": "Diesen Job löschen?", + "jobs.deleteDialogText": "Dies löscht den Job mit seinen Checkpoints und einem exportierten Modell. Das kann nicht rückgängig gemacht werden.", + "off.badge": "Für Sie nicht verfügbar", + "off.fineTune.title": "Ihr Konto kann keine Feinabstimmung", + "off.fineTune.text": "Feinabstimmung ist eine eigene Berechtigung, und dieses Konto hat sie nicht.", + "off.quantize.title": "Ihr Konto kann nicht quantisieren", + "off.quantize.text": "Quantisierung ist eine eigene Berechtigung, und dieses Konto hat sie nicht.", + "off.admin": "Ein Administrator kann sie unter Benutzer und Schlüssel aktivieren.", + "off.askAdmin": "Bitten Sie einen Administrator, sie für Ihr Konto zu aktivieren.", + "off.back": "Zur Startseite", + "landing.experimental": "Experimentell", + "run.unknownPercent": "Fortschritt noch nicht gemeldet", + "export.adminOnly.title": "Das Exportieren eines Modells ist Administratoren vorbehalten", + "export.adminOnly.text": "Beim Exportieren wird ein Modell zu diesem Server hinzugefügt, für alle, die es nutzen dürfen. Bitte einen Administrator, diesen Auftrag zu exportieren." +} diff --git a/addons/fine-tuning/ui/src/i18n/en.js b/addons/fine-tuning/ui/src/i18n/fine-tuning.en.json similarity index 99% rename from addons/fine-tuning/ui/src/i18n/en.js rename to addons/fine-tuning/ui/src/i18n/fine-tuning.en.json index 9b3229ba9b..d14df76741 100644 --- a/addons/fine-tuning/ui/src/i18n/en.js +++ b/addons/fine-tuning/ui/src/i18n/fine-tuning.en.json @@ -1,6 +1,4 @@ -// Generated once from the host locale file public/locales/en/tools.json; edit by hand from here on. -// The last three keys belong to this add-on. -export default { +{ "steps.label": "Progress", "steps.setup": "Set up", "steps.check": "Check", diff --git a/addons/fine-tuning/ui/src/i18n/fine-tuning.es.json b/addons/fine-tuning/ui/src/i18n/fine-tuning.es.json new file mode 100644 index 0000000000..d54edf461c --- /dev/null +++ b/addons/fine-tuning/ui/src/i18n/fine-tuning.es.json @@ -0,0 +1,295 @@ +{ + "steps.label": "Progreso", + "steps.setup": "Configurar", + "steps.check": "Comprobar", + "steps.run": "Ejecutar", + "steps.result": "Resultado", + "run.stop": "Detener", + "run.connecting": "Conectando con el job. Esperando el primer evento de progreso.", + "run.progress": "Progreso", + "run.stages": "Etapas", + "run.step": "paso", + "run.of": "de", + "run.epoch": "época", + "run.tokens": "tokens/s", + "run.eta": "faltan unos {{time}}", + "run.etaSeconds": "faltan unos {{seconds}} s", + "checks.heading": "Comprueba antes de empezar", + "checks.live": "Se actualiza mientras cambias la configuración.", + "checks.meterLabel": "{{used}} por ciento en uso, {{free}} libres de {{total}}", + "checks.openBackends": "Abrir Backends", + "checks.barOk": "Nada bloquea el inicio.", + "checks.barWarn_one": "{{count}} aviso. Aun así puedes empezar.", + "checks.barWarn_other": "{{count}} avisos. Aun así puedes empezar.", + "checks.title.model": "Modelo", + "checks.title.data": "Dataset", + "checks.title.backend": "Backend", + "checks.title.memory": "Memoria", + "checks.title.disk": "Disco", + "checks.title.reward": "Funciones de recompensa", + "checks.title.resume": "Reanudar", + "checks.title.access": "Acceso al modelo", + "checks.title.type": "Tipo de cuantización", + "checks.title.source": "Origen", + "checks.title.name": "Nombre", + "checks.fineTune.modelOk": "Entrenando {{model}}.", + "checks.fineTune.modelMissing": "Escribe el modelo que quieres entrenar.", + "checks.fineTune.dataOk": "Los datos vienen de {{source}}.", + "checks.fineTune.dataMissing": "Añade un dataset: un id de Hugging Face o un archivo subido.", + "checks.fineTune.backendOk": "{{name}} está instalado.", + "checks.fineTune.backendMissing": "No hay ningún backend de ajuste instalado, así que el job podría no arrancar.", + "checks.fineTune.backendOther": "{{name}} no está entre los backends de ajuste instalados ({{list}}).", + "checks.fineTune.backendUnknown": "No se pudieron leer los backends instalados.", + "checks.fineTune.memoryGpu": "{{name}}: {{free}} libres de {{total}}. Tamaño de lote {{batch}}, longitud de secuencia {{seq}}, acumulación de gradiente {{accum}}. Cuánta memoria necesita el job depende del modelo y de estos ajustes, y LocalAI no lo estima antes de que el job arranque.", + "checks.fineTune.memoryCluster": "{{total}} en el mejor nodo ({{node}}). Tamaño de lote {{batch}}, longitud de secuencia {{seq}}. Cuánta memoria necesita el job depende del modelo y de estos ajustes.", + "checks.fineTune.memoryCpu": "No se encontró ninguna GPU. El entrenamiento usa la CPU y {{free}} de {{total}} de RAM están libres. Espera que sea lento. Tamaño de lote {{batch}}, longitud de secuencia {{seq}}.", + "checks.fineTune.memoryUnknown": "Esta cuenta no puede leer la memoria de la máquina. Tamaño de lote {{batch}}, longitud de secuencia {{seq}}. Cuánta memoria necesita el job depende del modelo y de estos ajustes.", + "checks.fineTune.disk": "{{free}} libres en el disco de modelos. Las descargas y los checkpoints necesitan espacio.", + "checks.fineTune.diskLow": "Solo {{free}} libres en el disco de modelos. Las descargas y los checkpoints necesitan espacio.", + "checks.fineTune.rewardMissing": "GRPO necesita al menos una función de recompensa. Añade una en Más opciones.", + "checks.fineTune.resume": "El entrenamiento continúa desde {{path}}.", + "checks.fineTune.accessNone": "No hay token definido. Un modelo restringido necesita un token de Hugging Face en Más opciones.", + "checks.fineTune.accessSet": "Hay un token de Hugging Face definido.", + "checks.quantize.modelOk": "Cuantizando {{model}}.", + "checks.quantize.modelMissing": "Escribe el modelo que quieres cuantizar.", + "checks.quantize.typeMissing": "Escribe el tipo de cuantización personalizado.", + "checks.quantize.backendOk": "{{name}} está instalado.", + "checks.quantize.backendMissing": "No hay ningún backend de cuantización instalado, así que el job podría no arrancar.", + "checks.quantize.backendOther": "{{name}} no está entre los backends de cuantización instalados ({{list}}).", + "checks.quantize.backendUnknown": "No se pudieron leer los backends instalados.", + "checks.quantize.memory": "{{free}} libres de {{total}} de RAM. La conversión se ejecuta en la CPU.", + "checks.quantize.disk": "{{free}} libres en el disco de modelos. La descarga, el archivo convertido y el resultado se escriben en disco.", + "checks.quantize.diskLow": "Solo {{free}} libres en el disco de modelos. La descarga, el archivo convertido y el resultado se escriben en disco.", + "checks.quantize.accessNone": "No hay token definido. Un modelo restringido necesita un token de Hugging Face en Más opciones.", + "checks.quantize.accessSet": "Hay un token de Hugging Face definido.", + "checks.import.sourceOk_huggingface": "Se reconoce como repositorio de Hugging Face. LocalAI revisa sus archivos al importar.", + "checks.import.sourceOk_url": "Formato de URL reconocido. Su contenido no se ha comprobado.", + "checks.import.sourceOk_config": "Se reconoce como archivo de configuración de un modelo. Se lee y se usa tal como está.", + "checks.import.sourceOk_oci": "Se reconoce como referencia de imagen OCI.", + "checks.import.sourceOk_ollama": "Se reconoce como referencia de modelo de Ollama.", + "checks.import.sourceOk_local": "Se reconoce como ruta en la máquina que ejecuta LocalAI.", + "checks.import.sourceOk": "El origen se reconoce.", + "checks.import.sourceUnknown": "Esto no parece ninguna forma que LocalAI conozca. Lo intentará igualmente y podría fallar.", + "checks.import.backendDownload": "{{name}} aún no está instalado. LocalAI lo descarga primero.", + "checks.import.backendChosen": "{{name}} está elegido e instalado.", + "checks.import.backendAuto": "LocalAI elige el backend según los archivos del origen cuando empieza la importación.", + "checks.import.nameTaken": "Ya hay instalado un modelo llamado {{name}}.", + "checks.import.nameFree": "{{name}} no está en uso.", + "checks.import.nameAuto": "El modelo toma el nombre del origen.", + "checks.import.disk": "{{free}} libres en el disco de modelos. El tamaño de la descarga se conoce cuando empieza la importación.", + "checks.import.memory_gpu": "Ahora hay {{free}} libres de memoria de GPU. Lo que necesita este modelo se conoce cuando empieza la importación.", + "checks.import.memory_ram": "Ahora hay {{free}} libres de RAM. Lo que necesita este modelo se conoce cuando empieza la importación.", + "checks.import.memory_cluster": "Hay {{free}} disponibles en el mejor nodo. Lo que necesita este modelo se conoce cuando empieza la importación.", + "checks.import.memory": "Ahora hay {{free}} libres de memoria. Lo que necesita este modelo se conoce cuando empieza la importación.", + "form.key": "Clave", + "form.value": "Valor", + "form.addOption": "Añadir opción", + "form.copy": "Copiar al portapapeles", + "form.more": "Más opciones", + "form.moreHint": "método, backend, adaptador, optimizador, evaluación, funciones de recompensa", + "form.baseModel": "Modelo base", + "form.model": "Modelo", + "form.modelHint": "Un id de Hugging Face, o una ruta en este host.", + "form.data": "Tus datos", + "form.dataset": "Origen del dataset", + "form.datasetHint": "Un id de dataset de Hugging Face, o sube un archivo.", + "form.split": "Partición", + "form.upload": "Subir un archivo en su lugar", + "form.uploadHint": ".json, .jsonl o .csv", + "form.removeFile": "Quitar el archivo", + "form.train": "Qué entrenar", + "form.kind": "Tipo de entrenamiento", + "form.kinds.adapter.title": "Adaptador", + "form.kinds.adapter.text": "Entrena un adaptador pequeño y deja intactos los pesos base. Es la opción habitual, y necesita poca memoria.", + "form.kinds.full.title": "Modelo completo", + "form.kinds.full.text": "Cambia cada peso. Necesita varias veces la memoria de un adaptador.", + "form.epochs": "Épocas", + "form.batch": "Tamaño de lote", + "form.lr": "Tasa de aprendizaje", + "form.backend": "Backend", + "form.method": "Método de entrenamiento", + "form.adapterKind": "Tipo de adaptador", + "form.rank": "Rango", + "form.alpha": "Alpha", + "form.dropout": "Dropout", + "form.gradAccum": "Pasos de acumulación de gradiente", + "form.warmup": "Pasos de calentamiento", + "form.seqLen": "Longitud máxima de secuencia", + "form.optimizer": "Optimizador", + "form.maxSteps": "Pasos máximos", + "form.maxStepsHint": "0 para automático.", + "form.saveSteps": "Pasos de guardado", + "form.saveLimit": "Límite total de guardados", + "form.saveLimitHint": "0 conserva cada checkpoint.", + "form.weightDecay": "Decaimiento de pesos", + "form.seed": "Semilla", + "form.seedHint": "0 elige una semilla aleatoria.", + "form.precision": "Precisión mixta", + "form.auto": "Automático", + "form.targetModules": "Módulos objetivo", + "form.targetModulesHint": "Separados por comas. Déjalo en blanco para el valor por defecto del backend.", + "form.gradCheckpointing": "Gradient checkpointing (más lento, usa menos memoria)", + "form.token": "Token de Hugging Face", + "form.tokenHint": "Solo hace falta para los modelos restringidos.", + "form.evalEnable": "Evaluar durante el entrenamiento", + "form.evalStrategy": "Estrategia de evaluación", + "form.evalSteps_": "Pasos", + "form.evalEpoch": "Época", + "form.evalSteps": "Pasos de evaluación", + "form.evalStepsHint": "0 coincide con los pasos de guardado.", + "form.evalSplit": "Partición de evaluación", + "form.evalDataset": "Dataset de evaluación", + "form.evalDatasetPlaceholder": "Un dataset separado", + "form.evalRatio": "Proporción de partición automática", + "form.rewards": "Funciones de recompensa (GRPO)", + "form.rewardsHint": "GRPO necesita al menos una función de recompensa. Elige las integradas o añade las tuyas.", + "form.resume": "Reanudar desde un checkpoint", + "form.clearCheckpoint": "Borrar el checkpoint", + "form.extra": "Opciones extra", + "form.extraHint": "Pares clave y valor propios del backend.", + "form.summaryFull": "modelo completo", + "form.summary_lora": "adaptador LoRA", + "form.summary_loha": "adaptador LoHa", + "form.summary_lokr": "adaptador LoKr", + "form.recipe": "Entrenar un {{kind}} de {{model}} con {{method}}, {{epochs}} épocas sobre {{data}}.", + "form.recipeEmpty": "Elige un modelo y un dataset para ver qué se entrenará.", + "rewards.format_reward": "Comprueba el formato ... seguido de la respuesta.", + "rewards.reasoning_accuracy_reward": "Compara el contenido de con la columna de respuestas del dataset.", + "rewards.length_reward": "Puntúa lo cerca que está la respuesta de una longitud objetivo.", + "rewards.xml_tag_reward": "Puntúa las etiquetas XML abiertas y cerradas correctamente.", + "rewards.no_repetition_reward": "Penaliza los n-gramas repetidos.", + "rewards.code_execution_reward": "Comprueba que los bloques de código Python tengan una sintaxis válida.", + "rewards.param_target_length": "Longitud objetivo", + "rewards.fnName": "Nombre de la función", + "rewards.fnBody": "Cuerpo de la función", + "rewards.fnHint": "Recibe completions y kwargs y debe devolver una lista de floats. Están disponibles re, math, json y string.", + "rewards.add": "Añadir", + "rewards.cancel": "Cancelar", + "rewards.custom": "Añadir una función de recompensa propia", + "fineTune.title": "Ajuste fino", + "fineTune.lede": "Entrena un adaptador con tus propios datos y luego expórtalo como un modelo con el que puedas chatear.", + "fineTune.importConfig": "Importar configuración", + "fineTune.exportConfig": "Exportar configuración", + "fineTune.newJob": "Nuevo job", + "fineTune.badConfig": "No se pudo leer ese archivo. Usa una configuración JSON exportada desde esta página.", + "fineTune.start": "Iniciar el ajuste fino", + "fineTune.starting": "Iniciando...", + "fineTune.resume": "Reanudar el entrenamiento", + "fineTune.resuming": "Reanudando desde el checkpoint:", + "fineTune.clear": "Limpiar", + "fineTune.barNote": "No se cambia nada hasta que inicies.", + "fineTune.jobMeta": "{{kind}} {{method}} en {{backend}}, iniciado {{started}}", + "fineTune.failed": "El entrenamiento falló", + "fineTune.failedAt": "El entrenamiento se detuvo en el paso {{step}} de {{total}}", + "fineTune.noMessage": "El backend no dio ningún mensaje.", + "fineTune.finished": "El entrenamiento terminó.", + "fineTune.finishedLoss": "El entrenamiento terminó. La última pérdida fue {{loss}}.", + "fineTune.stopped": "El job se detuvo. Se conserva un checkpoint si lo elegiste.", + "fineTune.tryTitle": "Qué hacer después", + "fineTune.tryText": "El servidor indicó que se acabó la memoria. Cada cambio de abajo se aplica a una copia de la configuración, y nada arranca hasta que pulses Iniciar.", + "fineTune.tryPlain": "Vuelve a abrir la configuración, cambia lo que indica el mensaje e inicia un job nuevo.", + "fineTune.fixBatch": "Bajar el tamaño de lote a 1", + "fineTune.fixCheckpointing": "Activar gradient checkpointing (más lento, usa menos memoria)", + "fineTune.retryChanges": "Abrir la configuración con estos cambios", + "fineTune.reuse": "Volver a abrir la configuración", + "fineTune.resumeFrom": "Reanudar desde el paso {{step}}", + "fineTune.status.queued": "En cola", + "fineTune.status.loading_model": "Cargando el modelo", + "fineTune.status.loading_dataset": "Cargando el dataset", + "fineTune.status.training": "Entrenando", + "fineTune.status.saving": "Guardando", + "fineTune.status.completed": "Completado", + "fineTune.status.failed": "Fallido", + "fineTune.status.stopped": "Detenido", + "stop.title": "¿Detener el entrenamiento?", + "stop.text": "Un checkpoint guardado ahora te permite reanudar o exportar lo aprendido hasta ahora.", + "stop.keep": "Detener y guardar un checkpoint", + "stop.discard": "Detener sin checkpoint", + "stop.cancel": "Seguir entrenando", + "stop.cancelDelete": "Cancelar", + "chart.loss": "Pérdida", + "chart.learning_rate": "Tasa de aprendizaje", + "chart.grad_norm": "Norma del gradiente", + "chart.metric": "Métrica", + "chart.step": "paso", + "chart.eval": "Pérdida de evaluación", + "chart.waiting": "Esperando más pasos.", + "chart.window": "Pasos {{from}} a {{to}} de {{total}}", + "chart.windowEval": "Pasos {{from}} a {{to}} de {{total}}. Los puntos vacíos son la pérdida de evaluación.", + "chart.aria": "{{metric}} a lo largo de los pasos de entrenamiento. El último valor es {{last}} en el paso {{step}}.", + "chart.readout": "paso {{step}}: {{value}}", + "chart.dataTitle": "Mostrar los números", + "chart.emptyText": "El gráfico se dibuja cuando el job ha informado de dos pasos.", + "log.title": "Registro", + "log.note": "Lo que el job ha informado desde que se abrió esta página.", + "log.only": "Solo avisos y errores", + "log.copy": "Copiar", + "log.copied": "Copiado", + "log.download": "Descargar", + "log.empty": "Aún no se ha informado de nada.", + "log.noneFiltered": "No hay avisos ni errores.", + "checkpoints.title": "Checkpoints", + "checkpoints.step": "Paso", + "checkpoints.epoch": "Época", + "checkpoints.loss": "Pérdida", + "checkpoints.path": "Ruta", + "checkpoints.actions": "Acciones", + "checkpoints.resume": "Reanudar", + "checkpoints.resumeTitle": "Reanudar el entrenamiento desde este checkpoint", + "checkpoints.export": "Exportar", + "checkpoints.exportTitle": "Exportar este checkpoint", + "export.blocked": "La exportación está desactivada en este servidor porque no comparte la carpeta de modelos con el host del complemento.", + "export.title": "Exportar como modelo", + "export.lede": "Exportar convierte el resultado en un modelo que LocalAI puede cargar.", + "export.checkpoint": "Checkpoint", + "export.final": "Modelo final (directorio de salida)", + "export.checkpointOption": "Paso {{step}} (pérdida {{loss}})", + "export.format": "Formato de exportación", + "export.lora": "Adaptador LoRA", + "export.merged16": "Fusionado (16 bits)", + "export.merged4": "Fusionado (4 bits)", + "export.quant": "Cuantización", + "export.name": "Nombre del modelo", + "export.nameHint": "Déjalo en blanco para generar uno a partir del job.", + "export.go": "Exportar", + "export.exporting": "Exportando...", + "export.working": "Exportación en curso...", + "export.done": "Modelo exportado y registrado como \"{{name}}\"", + "export.failed": "La exportación falló: {{message}}", + "export.unknown": "error desconocido", + "export.nextTitle": "{{name}} ya es un modelo", + "export.nextText": "Está registrado en LocalAI. Abre un chat con él o búscalo en Modelos.", + "export.chat": "Chatear con {{name}}", + "export.models": "Abrir en Modelos", + "export.archive": "Descargar el archivo", + "jobs.title": "Jobs anteriores", + "jobs.count_one": "{{count}} guardado en este servidor", + "jobs.count_other": "{{count}} guardados en este servidor", + "jobs.loading": "Cargando jobs", + "jobs.emptyTitle": "Aún no hay jobs de ajuste fino", + "jobs.emptyText": "Configura uno arriba. Aparece aquí con su estado, y puedes reutilizar su configuración más adelante.", + "jobs.model": "Modelo", + "jobs.kind": "Tipo", + "jobs.status": "Estado", + "jobs.started": "Iniciado", + "jobs.actions": "Acciones", + "jobs.open": "Abrir este job", + "jobs.reuse": "Reutilizar", + "jobs.reuseTitle": "Usar la configuración de este job para uno nuevo", + "jobs.delete": "Eliminar", + "jobs.deleteTitle": "Eliminar este job y sus datos", + "jobs.deleteDialogTitle": "¿Eliminar este job?", + "jobs.deleteDialogText": "Esto elimina el job con sus checkpoints y cualquier modelo exportado. No se puede deshacer.", + "off.badge": "No disponible para ti", + "off.fineTune.title": "Tu cuenta no puede hacer ajuste fino", + "off.fineTune.text": "El ajuste fino es un permiso aparte, y esta cuenta no lo tiene.", + "off.quantize.title": "Tu cuenta no puede cuantizar", + "off.quantize.text": "La cuantización es un permiso aparte, y esta cuenta no lo tiene.", + "off.admin": "Un administrador puede activarlo en Usuarios y claves.", + "off.askAdmin": "Pide a un administrador que lo active para tu cuenta.", + "off.back": "Ir al inicio", + "landing.experimental": "Experimental", + "run.unknownPercent": "Progreso aún no informado", + "export.adminOnly.title": "Exportar un modelo es cosa de administradores", + "export.adminOnly.text": "Exportar añade un modelo a este servidor para todos los que pueden usarlo. Pide a un administrador que exporte este trabajo." +} diff --git a/addons/fine-tuning/ui/src/i18n/fine-tuning.fr.json b/addons/fine-tuning/ui/src/i18n/fine-tuning.fr.json new file mode 100644 index 0000000000..52cf58e2ce --- /dev/null +++ b/addons/fine-tuning/ui/src/i18n/fine-tuning.fr.json @@ -0,0 +1,295 @@ +{ + "steps.label": "Progression", + "steps.setup": "Configuration", + "steps.check": "Vérification", + "steps.run": "Exécution", + "steps.result": "Résultat", + "run.stop": "Arrêter", + "run.connecting": "Connexion au job. En attente du premier événement de progression.", + "run.progress": "Progression", + "run.stages": "Étapes", + "run.step": "étape", + "run.of": "sur", + "run.epoch": "époque", + "run.tokens": "jetons/s", + "run.eta": "environ {{time}} restantes", + "run.etaSeconds": "environ {{seconds}} s restantes", + "checks.heading": "Vérifier avant de commencer", + "checks.live": "Se met à jour à mesure que vous modifiez la configuration.", + "checks.meterLabel": "{{used}} pour cent utilisés, {{free}} libres sur {{total}}", + "checks.openBackends": "Ouvrir Backends", + "checks.barOk": "Rien ne bloque le démarrage.", + "checks.barWarn_one": "{{count}} avertissement. Vous pouvez tout de même démarrer.", + "checks.barWarn_other": "{{count}} avertissements. Vous pouvez tout de même démarrer.", + "checks.title.model": "Modèle", + "checks.title.data": "Jeu de données", + "checks.title.backend": "Backend", + "checks.title.memory": "Mémoire", + "checks.title.disk": "Disque", + "checks.title.reward": "Fonctions de récompense", + "checks.title.resume": "Reprise", + "checks.title.access": "Accès au modèle", + "checks.title.type": "Type de quantification", + "checks.title.source": "Source", + "checks.title.name": "Nom", + "checks.fineTune.modelOk": "Entraînement de {{model}}.", + "checks.fineTune.modelMissing": "Saisissez le modèle à entraîner.", + "checks.fineTune.dataOk": "Les données viennent de {{source}}.", + "checks.fineTune.dataMissing": "Ajoutez un jeu de données : un identifiant Hugging Face ou un fichier envoyé.", + "checks.fineTune.backendOk": "{{name}} est installé.", + "checks.fineTune.backendMissing": "Aucun backend de fine-tuning n'est installé, la tâche risque donc de ne pas démarrer.", + "checks.fineTune.backendOther": "{{name}} ne fait pas partie des backends de fine-tuning installés ({{list}}).", + "checks.fineTune.backendUnknown": "Impossible de lire les backends installés.", + "checks.fineTune.memoryGpu": "{{name}} : {{free}} libres sur {{total}}. Taille de batch {{batch}}, longueur de séquence {{seq}}, accumulation de gradient {{accum}}. La mémoire nécessaire dépend du modèle et de ces réglages, et LocalAI ne l'estime pas avant le démarrage de la tâche.", + "checks.fineTune.memoryCluster": "{{total}} sur le meilleur nœud ({{node}}). Taille de batch {{batch}}, longueur de séquence {{seq}}. La mémoire nécessaire dépend du modèle et de ces réglages.", + "checks.fineTune.memoryCpu": "Aucun GPU trouvé. L'entraînement utilise le CPU, et {{free}} de RAM sur {{total}} sont libres. Attendez-vous à de la lenteur. Taille de batch {{batch}}, longueur de séquence {{seq}}.", + "checks.fineTune.memoryUnknown": "Ce compte ne peut pas lire la mémoire de la machine. Taille de batch {{batch}}, longueur de séquence {{seq}}. La mémoire nécessaire dépend du modèle et de ces réglages.", + "checks.fineTune.disk": "{{free}} libres sur le disque des modèles. Les téléchargements et les checkpoints ont besoin d'espace.", + "checks.fineTune.diskLow": "Seulement {{free}} libres sur le disque des modèles. Les téléchargements et les checkpoints ont besoin d'espace.", + "checks.fineTune.rewardMissing": "GRPO nécessite au moins une fonction de récompense. Ajoutez-en une dans Plus d'options.", + "checks.fineTune.resume": "L'entraînement reprend depuis {{path}}.", + "checks.fineTune.accessNone": "Aucun jeton défini. Un modèle protégé nécessite un jeton Hugging Face dans Plus d'options.", + "checks.fineTune.accessSet": "Un jeton Hugging Face est défini.", + "checks.quantize.modelOk": "Quantification de {{model}}.", + "checks.quantize.modelMissing": "Saisissez le modèle à quantifier.", + "checks.quantize.typeMissing": "Saisissez le type de quantification personnalisé.", + "checks.quantize.backendOk": "{{name}} est installé.", + "checks.quantize.backendMissing": "Aucun backend de quantification n'est installé, la tâche risque donc de ne pas démarrer.", + "checks.quantize.backendOther": "{{name}} ne fait pas partie des backends de quantification installés ({{list}}).", + "checks.quantize.backendUnknown": "Impossible de lire les backends installés.", + "checks.quantize.memory": "{{free}} de RAM sur {{total}} sont libres. La conversion se fait sur le CPU.", + "checks.quantize.disk": "{{free}} libres sur le disque des modèles. Le téléchargement, le fichier converti et le résultat sont écrits sur le disque.", + "checks.quantize.diskLow": "Seulement {{free}} libres sur le disque des modèles. Le téléchargement, le fichier converti et le résultat sont écrits sur le disque.", + "checks.quantize.accessNone": "Aucun jeton défini. Un modèle protégé nécessite un jeton Hugging Face dans Plus d'options.", + "checks.quantize.accessSet": "Un jeton Hugging Face est défini.", + "checks.import.sourceOk_huggingface": "Reconnu comme dépôt Hugging Face. LocalAI examine ses fichiers lors de l'import.", + "checks.import.sourceOk_url": "Format d'URL reconnu. Son contenu n'a pas été vérifié.", + "checks.import.sourceOk_config": "Reconnu comme fichier de configuration de modèle. Il est lu et utilisé tel quel.", + "checks.import.sourceOk_oci": "Reconnu comme référence d'image OCI.", + "checks.import.sourceOk_ollama": "Reconnu comme référence de modèle Ollama.", + "checks.import.sourceOk_local": "Reconnu comme chemin sur la machine qui exécute LocalAI.", + "checks.import.sourceOk": "La source est reconnue.", + "checks.import.sourceUnknown": "Ce format ne correspond à aucun format connu de LocalAI. L'import essaiera quand même, et peut échouer.", + "checks.import.backendDownload": "{{name}} n'est pas encore installé. LocalAI le télécharge d'abord.", + "checks.import.backendChosen": "{{name}} est choisi et installé.", + "checks.import.backendAuto": "LocalAI choisit le backend d'après les fichiers de la source au démarrage de l'import.", + "checks.import.nameTaken": "Un modèle nommé {{name}} est déjà installé.", + "checks.import.nameFree": "{{name}} n'est pas utilisé.", + "checks.import.nameAuto": "Le modèle prend le nom de la source.", + "checks.import.disk": "{{free}} libres sur le disque des modèles. La taille du téléchargement est connue au démarrage de l'import.", + "checks.import.memory_gpu": "{{free}} de mémoire GPU sont libres maintenant. Ce dont ce modèle a besoin est connu au démarrage de l'import.", + "checks.import.memory_ram": "{{free}} de RAM sont libres maintenant. Ce dont ce modèle a besoin est connu au démarrage de l'import.", + "checks.import.memory_cluster": "{{free}} sont disponibles sur le meilleur nœud. Ce dont ce modèle a besoin est connu au démarrage de l'import.", + "checks.import.memory": "{{free}} de mémoire sont libres maintenant. Ce dont ce modèle a besoin est connu au démarrage de l'import.", + "form.key": "Clé", + "form.value": "Valeur", + "form.addOption": "Ajouter une option", + "form.copy": "Copier dans le presse-papiers", + "form.more": "Plus d'options", + "form.moreHint": "méthode, backend, adaptateur, optimiseur, évaluation, fonctions de récompense", + "form.baseModel": "Modèle de base", + "form.model": "Modèle", + "form.modelHint": "Un identifiant Hugging Face, ou un chemin sur cette machine.", + "form.data": "Vos données", + "form.dataset": "Source du jeu de données", + "form.datasetHint": "Un identifiant de jeu de données Hugging Face, ou envoyez un fichier.", + "form.split": "Découpage", + "form.upload": "Envoyer un fichier à la place", + "form.uploadHint": ".json, .jsonl ou .csv", + "form.removeFile": "Retirer le fichier", + "form.train": "Ce qu'il faut entraîner", + "form.kind": "Type d'entraînement", + "form.kinds.adapter.title": "Adaptateur", + "form.kinds.adapter.text": "Entraîne un petit adaptateur et laisse les poids de base intacts. Le choix habituel, qui demande peu de mémoire.", + "form.kinds.full.title": "Modèle complet", + "form.kinds.full.text": "Modifie chaque poids. Il demande plusieurs fois la mémoire d'un adaptateur.", + "form.epochs": "Époques", + "form.batch": "Taille de batch", + "form.lr": "Taux d'apprentissage", + "form.backend": "Backend", + "form.method": "Méthode d'entraînement", + "form.adapterKind": "Type d'adaptateur", + "form.rank": "Rang", + "form.alpha": "Alpha", + "form.dropout": "Dropout", + "form.gradAccum": "Pas d'accumulation de gradient", + "form.warmup": "Pas de préchauffage", + "form.seqLen": "Longueur maximale de séquence", + "form.optimizer": "Optimiseur", + "form.maxSteps": "Nombre maximal de pas", + "form.maxStepsHint": "0 pour automatique.", + "form.saveSteps": "Pas de sauvegarde", + "form.saveLimit": "Limite totale de sauvegardes", + "form.saveLimitHint": "0 conserve chaque checkpoint.", + "form.weightDecay": "Décroissance des poids", + "form.seed": "Graine", + "form.seedHint": "0 choisit une graine aléatoire.", + "form.precision": "Précision mixte", + "form.auto": "Automatique", + "form.targetModules": "Modules cibles", + "form.targetModulesHint": "Séparés par des virgules. Laissez vide pour la valeur par défaut du backend.", + "form.gradCheckpointing": "Gradient checkpointing (plus lent, utilise moins de mémoire)", + "form.token": "Jeton Hugging Face", + "form.tokenHint": "Nécessaire uniquement pour les modèles protégés.", + "form.evalEnable": "Évaluer pendant l'entraînement", + "form.evalStrategy": "Stratégie d'évaluation", + "form.evalSteps_": "Pas", + "form.evalEpoch": "Époque", + "form.evalSteps": "Pas d'évaluation", + "form.evalStepsHint": "0 correspond aux pas de sauvegarde.", + "form.evalSplit": "Découpage pour l'évaluation", + "form.evalDataset": "Jeu de données d'évaluation", + "form.evalDatasetPlaceholder": "Un jeu de données séparé", + "form.evalRatio": "Ratio de découpage automatique", + "form.rewards": "Fonctions de récompense (GRPO)", + "form.rewardsHint": "GRPO nécessite au moins une fonction de récompense. Choisissez les fonctions intégrées ou ajoutez les vôtres.", + "form.resume": "Reprendre depuis un checkpoint", + "form.clearCheckpoint": "Effacer le checkpoint", + "form.extra": "Options supplémentaires", + "form.extraHint": "Paires clé-valeur propres au backend.", + "form.summaryFull": "modèle complet", + "form.summary_lora": "adaptateur LoRA", + "form.summary_loha": "adaptateur LoHa", + "form.summary_lokr": "adaptateur LoKr", + "form.recipe": "Entraîner un {{kind}} de {{model}} avec {{method}}, {{epochs}} époques sur {{data}}.", + "form.recipeEmpty": "Choisissez un modèle et un jeu de données pour voir ce qui sera entraîné.", + "rewards.format_reward": "Vérifie le format ... suivi de la réponse.", + "rewards.reasoning_accuracy_reward": "Compare le contenu de à la colonne de réponses du jeu de données.", + "rewards.length_reward": "Note la proximité de la réponse avec une longueur cible.", + "rewards.xml_tag_reward": "Note les balises XML correctement ouvertes et fermées.", + "rewards.no_repetition_reward": "Pénalise les n-grammes répétés.", + "rewards.code_execution_reward": "Vérifie que les blocs de code Python ont une syntaxe valide.", + "rewards.param_target_length": "Longueur cible", + "rewards.fnName": "Nom de la fonction", + "rewards.fnBody": "Corps de la fonction", + "rewards.fnHint": "Reçoit completions et kwargs et doit renvoyer une liste de flottants. re, math, json et string sont disponibles.", + "rewards.add": "Ajouter", + "rewards.cancel": "Annuler", + "rewards.custom": "Ajouter une fonction de récompense personnalisée", + "fineTune.title": "Fine-tuning", + "fineTune.lede": "Entraînez un adaptateur sur vos propres données, puis exportez-le comme modèle avec lequel vous pouvez discuter.", + "fineTune.importConfig": "Importer la configuration", + "fineTune.exportConfig": "Exporter la configuration", + "fineTune.newJob": "Nouvelle tâche", + "fineTune.badConfig": "Impossible de lire ce fichier. Utilisez une configuration JSON exportée depuis cette page.", + "fineTune.start": "Lancer le fine-tuning", + "fineTune.starting": "Démarrage...", + "fineTune.resume": "Reprendre l'entraînement", + "fineTune.resuming": "Reprise depuis le checkpoint :", + "fineTune.clear": "Effacer", + "fineTune.barNote": "Rien n'est modifié tant que vous ne lancez pas.", + "fineTune.jobMeta": "{{kind}} {{method}} sur {{backend}}, démarré {{started}}", + "fineTune.failed": "Échec de l'entraînement", + "fineTune.failedAt": "Entraînement arrêté à l'étape {{step}} sur {{total}}", + "fineTune.noMessage": "Le backend n'a donné aucun message.", + "fineTune.finished": "Entraînement terminé.", + "fineTune.finishedLoss": "Entraînement terminé. La dernière perte était {{loss}}.", + "fineTune.stopped": "La tâche a été arrêtée. Un checkpoint est conservé si vous l'avez choisi.", + "fineTune.tryTitle": "Que faire ensuite", + "fineTune.tryText": "Le serveur indique que la mémoire est épuisée. Chaque modification ci-dessous est appliquée à une copie de la configuration, et rien ne démarre tant que vous n'appuyez pas sur Lancer.", + "fineTune.tryPlain": "Rouvrez la configuration, modifiez ce que le message indique et lancez une nouvelle tâche.", + "fineTune.fixBatch": "Réduire la taille de batch à 1", + "fineTune.fixCheckpointing": "Activer le gradient checkpointing (plus lent, utilise moins de mémoire)", + "fineTune.retryChanges": "Ouvrir la configuration avec ces modifications", + "fineTune.reuse": "Rouvrir la configuration", + "fineTune.resumeFrom": "Reprendre à partir de l'étape {{step}}", + "fineTune.status.queued": "En file d'attente", + "fineTune.status.loading_model": "Chargement du modèle", + "fineTune.status.loading_dataset": "Chargement du jeu de données", + "fineTune.status.training": "Entraînement", + "fineTune.status.saving": "Sauvegarde", + "fineTune.status.completed": "Terminée", + "fineTune.status.failed": "Échouée", + "fineTune.status.stopped": "Arrêtée", + "stop.title": "Arrêter l'entraînement ?", + "stop.text": "Un checkpoint sauvegardé maintenant permet de reprendre ou d'exporter ce qui a été appris jusqu'ici.", + "stop.keep": "Arrêter et garder un checkpoint", + "stop.discard": "Arrêter sans checkpoint", + "stop.cancel": "Continuer l'entraînement", + "stop.cancelDelete": "Annuler", + "chart.loss": "Perte", + "chart.learning_rate": "Taux d'apprentissage", + "chart.grad_norm": "Norme du gradient", + "chart.metric": "Métrique", + "chart.step": "étape", + "chart.eval": "Perte d'évaluation", + "chart.waiting": "En attente de plus d'étapes.", + "chart.window": "Étapes {{from}} à {{to}} sur {{total}}", + "chart.windowEval": "Étapes {{from}} à {{to}} sur {{total}}. Les points vides sont la perte d'évaluation.", + "chart.aria": "{{metric}} au fil des étapes d'entraînement. La dernière valeur est {{last}} à l'étape {{step}}.", + "chart.readout": "étape {{step}} : {{value}}", + "chart.dataTitle": "Afficher les valeurs", + "chart.emptyText": "Le graphique se trace lorsque la tâche a signalé deux étapes.", + "log.title": "Journal", + "log.note": "Ce que la tâche a signalé depuis l'ouverture de cette page.", + "log.only": "Avertissements et erreurs uniquement", + "log.copy": "Copier", + "log.copied": "Copié", + "log.download": "Télécharger", + "log.empty": "Rien n'a encore été signalé.", + "log.noneFiltered": "Aucun avertissement ni erreur.", + "checkpoints.title": "Checkpoints", + "checkpoints.step": "Étape", + "checkpoints.epoch": "Époque", + "checkpoints.loss": "Perte", + "checkpoints.path": "Chemin", + "checkpoints.actions": "Actions", + "checkpoints.resume": "Reprendre", + "checkpoints.resumeTitle": "Reprendre l'entraînement depuis ce checkpoint", + "checkpoints.export": "Exporter", + "checkpoints.exportTitle": "Exporter ce checkpoint", + "export.blocked": "L'export est désactivé sur ce serveur, car il ne partage pas le dossier des modèles avec l'hôte de l'extension.", + "export.title": "Exporter comme modèle", + "export.lede": "L'export transforme le résultat en modèle que LocalAI peut charger.", + "export.checkpoint": "Checkpoint", + "export.final": "Modèle final (dossier de sortie)", + "export.checkpointOption": "Étape {{step}} (perte {{loss}})", + "export.format": "Format d'export", + "export.lora": "Adaptateur LoRA", + "export.merged16": "Fusionné (16 bits)", + "export.merged4": "Fusionné (4 bits)", + "export.quant": "Quantification", + "export.name": "Nom du modèle", + "export.nameHint": "Laissez vide pour en générer un à partir de la tâche.", + "export.go": "Exporter", + "export.exporting": "Export en cours...", + "export.working": "Export en cours...", + "export.done": "Modèle exporté et enregistré sous « {{name}} »", + "export.failed": "Échec de l'export : {{message}}", + "export.unknown": "erreur inconnue", + "export.nextTitle": "{{name}} est maintenant un modèle", + "export.nextText": "Il est enregistré dans LocalAI. Ouvrez une discussion avec lui, ou retrouvez-le dans Modèles.", + "export.chat": "Discuter avec {{name}}", + "export.models": "Ouvrir dans Modèles", + "export.archive": "Télécharger l'archive", + "jobs.title": "Tâches précédentes", + "jobs.count_one": "{{count}} conservée sur ce serveur", + "jobs.count_other": "{{count}} conservées sur ce serveur", + "jobs.loading": "Chargement des tâches", + "jobs.emptyTitle": "Aucune tâche de fine-tuning pour le moment", + "jobs.emptyText": "Configurez-en une ci-dessus. Elle apparaît ici avec son état, et vous pourrez réutiliser sa configuration plus tard.", + "jobs.model": "Modèle", + "jobs.kind": "Type", + "jobs.status": "État", + "jobs.started": "Démarré", + "jobs.actions": "Actions", + "jobs.open": "Ouvrir cette tâche", + "jobs.reuse": "Réutiliser", + "jobs.reuseTitle": "Utiliser la configuration de cette tâche pour une nouvelle tâche", + "jobs.delete": "Supprimer", + "jobs.deleteTitle": "Supprimer cette tâche et ses données", + "jobs.deleteDialogTitle": "Supprimer cette tâche ?", + "jobs.deleteDialogText": "Cela supprime la tâche avec ses checkpoints et tout modèle exporté. Action irréversible.", + "off.badge": "Non disponible pour vous", + "off.fineTune.title": "Votre compte ne peut pas faire de fine-tuning", + "off.fineTune.text": "Le fine-tuning est une autorisation distincte, que ce compte n'a pas.", + "off.quantize.title": "Votre compte ne peut pas quantifier", + "off.quantize.text": "La quantification est une autorisation distincte, que ce compte n'a pas.", + "off.admin": "Un administrateur peut l'activer dans Utilisateurs et clés.", + "off.askAdmin": "Demandez à un administrateur de l'activer pour votre compte.", + "off.back": "Aller à l'accueil", + "landing.experimental": "Expérimental", + "run.unknownPercent": "Progression pas encore communiquée", + "export.adminOnly.title": "L'export d'un modèle est réservé aux administrateurs", + "export.adminOnly.text": "L'export ajoute un modèle à ce serveur pour tous ceux qui peuvent l'utiliser. Demandez à un administrateur d'exporter cette tâche." +} diff --git a/addons/fine-tuning/ui/src/i18n/fine-tuning.id.json b/addons/fine-tuning/ui/src/i18n/fine-tuning.id.json new file mode 100644 index 0000000000..ba93db6420 --- /dev/null +++ b/addons/fine-tuning/ui/src/i18n/fine-tuning.id.json @@ -0,0 +1,295 @@ +{ + "steps.label": "Progres", + "steps.setup": "Siapkan", + "steps.check": "Periksa", + "steps.run": "Jalankan", + "steps.result": "Hasil", + "run.stop": "Hentikan", + "run.connecting": "Menghubungkan ke job. Menunggu event progres pertama.", + "run.progress": "Progres", + "run.stages": "Tahap", + "run.step": "langkah", + "run.of": "dari", + "run.epoch": "epoch", + "run.tokens": "token/s", + "run.eta": "sekitar {{time}} lagi", + "run.etaSeconds": "sekitar {{seconds}} d lagi", + "checks.heading": "Periksa sebelum memulai", + "checks.live": "Diperbarui saat Anda mengubah pengaturan.", + "checks.meterLabel": "{{used}} persen terpakai, {{free}} tersedia dari {{total}}", + "checks.openBackends": "Buka Backend", + "checks.barOk": "Tidak ada yang menghalangi permulaan.", + "checks.barWarn_one": "{{count}} peringatan. Anda tetap bisa memulai.", + "checks.barWarn_other": "{{count}} peringatan. Anda tetap bisa memulai.", + "checks.title.model": "Model", + "checks.title.data": "Dataset", + "checks.title.backend": "Backend", + "checks.title.memory": "Memori", + "checks.title.disk": "Disk", + "checks.title.reward": "Fungsi reward", + "checks.title.resume": "Melanjutkan", + "checks.title.access": "Akses model", + "checks.title.type": "Tipe kuantisasi", + "checks.title.source": "Sumber", + "checks.title.name": "Nama", + "checks.fineTune.modelOk": "Melatih {{model}}.", + "checks.fineTune.modelMissing": "Masukkan model yang akan dilatih.", + "checks.fineTune.dataOk": "Data berasal dari {{source}}.", + "checks.fineTune.dataMissing": "Tambahkan dataset: id Hugging Face atau file yang diunggah.", + "checks.fineTune.backendOk": "{{name}} sudah terpasang.", + "checks.fineTune.backendMissing": "Belum ada backend fine-tuning yang terpasang, jadi job mungkin gagal dimulai.", + "checks.fineTune.backendOther": "{{name}} tidak termasuk backend fine-tuning yang terpasang ({{list}}).", + "checks.fineTune.backendUnknown": "Backend yang terpasang tidak dapat dibaca.", + "checks.fineTune.memoryGpu": "{{name}}: {{free}} dari {{total}} tersedia. Ukuran batch {{batch}}, panjang sekuens {{seq}}, akumulasi gradien {{accum}}. Kebutuhan job bergantung pada model dan pengaturan ini, dan LocalAI tidak memperkirakannya sebelum job dimulai.", + "checks.fineTune.memoryCluster": "{{total}} di node terbaik ({{node}}). Ukuran batch {{batch}}, panjang sekuens {{seq}}. Kebutuhan job bergantung pada model dan pengaturan ini.", + "checks.fineTune.memoryCpu": "GPU tidak ditemukan. Pelatihan menggunakan CPU dan {{free}} dari {{total}} RAM tersedia. Diperkirakan lambat. Ukuran batch {{batch}}, panjang sekuens {{seq}}.", + "checks.fineTune.memoryUnknown": "Akun ini tidak dapat membaca memori mesin. Ukuran batch {{batch}}, panjang sekuens {{seq}}. Kebutuhan job bergantung pada model dan pengaturan ini.", + "checks.fineTune.disk": "{{free}} tersedia di disk model. Unduhan dan checkpoint membutuhkan ruang.", + "checks.fineTune.diskLow": "Hanya {{free}} tersedia di disk model. Unduhan dan checkpoint membutuhkan ruang.", + "checks.fineTune.rewardMissing": "GRPO membutuhkan minimal satu fungsi reward. Tambahkan satu di Opsi lainnya.", + "checks.fineTune.resume": "Pelatihan dilanjutkan dari {{path}}.", + "checks.fineTune.accessNone": "Token belum diatur. Model yang dibatasi membutuhkan token Hugging Face di Opsi lainnya.", + "checks.fineTune.accessSet": "Token Hugging Face sudah diatur.", + "checks.quantize.modelOk": "Mengkuantisasi {{model}}.", + "checks.quantize.modelMissing": "Masukkan model yang akan dikuantisasi.", + "checks.quantize.typeMissing": "Masukkan tipe kuantisasi kustom.", + "checks.quantize.backendOk": "{{name}} sudah terpasang.", + "checks.quantize.backendMissing": "Belum ada backend kuantisasi yang terpasang, jadi job mungkin gagal dimulai.", + "checks.quantize.backendOther": "{{name}} tidak termasuk backend kuantisasi yang terpasang ({{list}}).", + "checks.quantize.backendUnknown": "Backend yang terpasang tidak dapat dibaca.", + "checks.quantize.memory": "{{free}} dari {{total}} RAM tersedia. Konversi berjalan di CPU.", + "checks.quantize.disk": "{{free}} tersedia di disk model. Unduhan, file hasil konversi, dan hasilnya ditulis ke disk.", + "checks.quantize.diskLow": "Hanya {{free}} tersedia di disk model. Unduhan, file hasil konversi, dan hasilnya ditulis ke disk.", + "checks.quantize.accessNone": "Token belum diatur. Model yang dibatasi membutuhkan token Hugging Face di Opsi lainnya.", + "checks.quantize.accessSet": "Token Hugging Face sudah diatur.", + "checks.import.sourceOk_huggingface": "Dikenali sebagai repositori Hugging Face. LocalAI memeriksa file-filenya saat Anda mengimpor.", + "checks.import.sourceOk_url": "Format URL dikenali. Isinya belum diperiksa.", + "checks.import.sourceOk_config": "Dikenali sebagai file konfigurasi model. File dibaca dan digunakan apa adanya.", + "checks.import.sourceOk_oci": "Dikenali sebagai referensi image OCI.", + "checks.import.sourceOk_ollama": "Dikenali sebagai referensi model Ollama.", + "checks.import.sourceOk_local": "Dikenali sebagai path di mesin yang menjalankan LocalAI.", + "checks.import.sourceOk": "Sumber dikenali.", + "checks.import.sourceUnknown": "Ini tidak tampak seperti format yang diketahui LocalAI. Tetap akan dicoba, dan mungkin gagal.", + "checks.import.backendDownload": "{{name}} belum terpasang. LocalAI mengunduhnya terlebih dahulu.", + "checks.import.backendChosen": "{{name}} dipilih dan terpasang.", + "checks.import.backendAuto": "LocalAI memilih backend dari file sumber saat impor dimulai.", + "checks.import.nameTaken": "Model bernama {{name}} sudah terpasang.", + "checks.import.nameFree": "{{name}} belum digunakan.", + "checks.import.nameAuto": "Model dinamai sesuai sumbernya.", + "checks.import.disk": "{{free}} tersedia di disk model. Ukuran unduhan diketahui saat impor dimulai.", + "checks.import.memory_gpu": "{{free}} memori GPU tersedia saat ini. Kebutuhan model ini diketahui saat impor dimulai.", + "checks.import.memory_ram": "{{free}} RAM tersedia saat ini. Kebutuhan model ini diketahui saat impor dimulai.", + "checks.import.memory_cluster": "{{free}} tersedia di node terbaik. Kebutuhan model ini diketahui saat impor dimulai.", + "checks.import.memory": "{{free}} memori tersedia saat ini. Kebutuhan model ini diketahui saat impor dimulai.", + "form.key": "Key", + "form.value": "Value", + "form.addOption": "Tambah opsi", + "form.copy": "Salin ke clipboard", + "form.more": "Opsi lainnya", + "form.moreHint": "metode, backend, adapter, optimizer, evaluasi, fungsi reward", + "form.baseModel": "Model dasar", + "form.model": "Model", + "form.modelHint": "Id Hugging Face, atau path di host ini.", + "form.data": "Data Anda", + "form.dataset": "Sumber dataset", + "form.datasetHint": "Id dataset Hugging Face, atau unggah file.", + "form.split": "Pembagian", + "form.upload": "Unggah file sebagai gantinya", + "form.uploadHint": ".json, .jsonl atau .csv", + "form.removeFile": "Hapus file", + "form.train": "Yang akan dilatih", + "form.kind": "Jenis pelatihan", + "form.kinds.adapter.title": "Adapter", + "form.kinds.adapter.text": "Melatih adapter kecil dan membiarkan bobot dasar tidak berubah. Pilihan yang umum, dan membutuhkan sedikit memori.", + "form.kinds.full.title": "Model penuh", + "form.kinds.full.text": "Mengubah setiap bobot. Membutuhkan memori beberapa kali lipat dari adapter.", + "form.epochs": "Epoch", + "form.batch": "Ukuran batch", + "form.lr": "Learning rate", + "form.backend": "Backend", + "form.method": "Metode pelatihan", + "form.adapterKind": "Jenis adapter", + "form.rank": "Rank", + "form.alpha": "Alpha", + "form.dropout": "Dropout", + "form.gradAccum": "Langkah akumulasi gradien", + "form.warmup": "Langkah warmup", + "form.seqLen": "Panjang sekuens maksimum", + "form.optimizer": "Optimizer", + "form.maxSteps": "Langkah maksimum", + "form.maxStepsHint": "0 untuk otomatis.", + "form.saveSteps": "Langkah simpan", + "form.saveLimit": "Batas total simpan", + "form.saveLimitHint": "0 menyimpan setiap checkpoint.", + "form.weightDecay": "Weight decay", + "form.seed": "Seed", + "form.seedHint": "0 memilih seed acak.", + "form.precision": "Presisi campuran", + "form.auto": "Otomatis", + "form.targetModules": "Modul target", + "form.targetModulesHint": "Dipisahkan koma. Kosongkan untuk default backend.", + "form.gradCheckpointing": "Gradient checkpointing (lebih lambat, memakai lebih sedikit memori)", + "form.token": "Token Hugging Face", + "form.tokenHint": "Hanya dibutuhkan untuk model yang dibatasi.", + "form.evalEnable": "Evaluasi selama pelatihan", + "form.evalStrategy": "Strategi evaluasi", + "form.evalSteps_": "Langkah", + "form.evalEpoch": "Epoch", + "form.evalSteps": "Langkah evaluasi", + "form.evalStepsHint": "0 mengikuti langkah simpan.", + "form.evalSplit": "Pembagian evaluasi", + "form.evalDataset": "Dataset evaluasi", + "form.evalDatasetPlaceholder": "Dataset terpisah", + "form.evalRatio": "Rasio pembagian otomatis", + "form.rewards": "Fungsi reward (GRPO)", + "form.rewardsHint": "GRPO membutuhkan minimal satu fungsi reward. Pilih yang bawaan atau tambahkan sendiri.", + "form.resume": "Lanjutkan dari checkpoint", + "form.clearCheckpoint": "Hapus checkpoint", + "form.extra": "Opsi tambahan", + "form.extraHint": "Pasangan kunci dan nilai khusus backend.", + "form.summaryFull": "model penuh", + "form.summary_lora": "adapter LoRA", + "form.summary_loha": "adapter LoHa", + "form.summary_lokr": "adapter LoKr", + "form.recipe": "Latih {{kind}} dari {{model}} dengan {{method}}, {{epochs}} epoch pada {{data}}.", + "form.recipeEmpty": "Pilih model dan dataset untuk melihat apa yang akan dilatih.", + "rewards.format_reward": "Memeriksa format ... lalu jawaban.", + "rewards.reasoning_accuracy_reward": "Membandingkan isi dengan kolom jawaban dataset.", + "rewards.length_reward": "Menilai seberapa dekat jawaban dengan panjang target.", + "rewards.xml_tag_reward": "Menilai tag XML yang dibuka dan ditutup dengan benar.", + "rewards.no_repetition_reward": "Memberi penalti pada n-gram yang berulang.", + "rewards.code_execution_reward": "Memeriksa bahwa blok kode Python memiliki sintaks yang valid.", + "rewards.param_target_length": "Panjang target", + "rewards.fnName": "Nama fungsi", + "rewards.fnBody": "Isi fungsi", + "rewards.fnHint": "Menerima completions dan kwargs dan harus mengembalikan daftar float. re, math, json dan string tersedia.", + "rewards.add": "Tambah", + "rewards.cancel": "Batal", + "rewards.custom": "Tambahkan fungsi reward kustom", + "fineTune.title": "Fine-tune", + "fineTune.lede": "Latih adapter dengan data Anda sendiri, lalu ekspor sebagai model yang bisa Anda ajak chat.", + "fineTune.importConfig": "Impor konfigurasi", + "fineTune.exportConfig": "Ekspor konfigurasi", + "fineTune.newJob": "Job baru", + "fineTune.badConfig": "File tidak dapat dibaca. Gunakan konfigurasi JSON yang diekspor dari halaman ini.", + "fineTune.start": "Mulai fine-tuning", + "fineTune.starting": "Memulai...", + "fineTune.resume": "Lanjutkan pelatihan", + "fineTune.resuming": "Melanjutkan dari checkpoint:", + "fineTune.clear": "Bersihkan", + "fineTune.barNote": "Tidak ada yang berubah sampai Anda memulai.", + "fineTune.jobMeta": "{{kind}} {{method}} pada {{backend}}, dimulai {{started}}", + "fineTune.failed": "Pelatihan gagal", + "fineTune.failedAt": "Pelatihan berhenti pada langkah {{step}} dari {{total}}", + "fineTune.noMessage": "Backend tidak memberikan pesan.", + "fineTune.finished": "Pelatihan selesai.", + "fineTune.finishedLoss": "Pelatihan selesai. Loss terakhir adalah {{loss}}.", + "fineTune.stopped": "Job dihentikan. Checkpoint disimpan jika Anda memilih untuk menyimpannya.", + "fineTune.tryTitle": "Langkah berikutnya", + "fineTune.tryText": "Server melaporkan bahwa memori habis. Setiap perubahan di bawah diterapkan pada salinan pengaturan, dan tidak ada yang dimulai sampai Anda menekan Mulai.", + "fineTune.tryPlain": "Buka pengaturan lagi, ubah apa yang ditunjuk pesan, lalu mulai job baru.", + "fineTune.fixBatch": "Turunkan ukuran batch menjadi 1", + "fineTune.fixCheckpointing": "Aktifkan gradient checkpointing (lebih lambat, memakai lebih sedikit memori)", + "fineTune.retryChanges": "Buka pengaturan dengan perubahan ini", + "fineTune.reuse": "Buka pengaturan lagi", + "fineTune.resumeFrom": "Lanjutkan dari langkah {{step}}", + "fineTune.status.queued": "Antre", + "fineTune.status.loading_model": "Memuat model", + "fineTune.status.loading_dataset": "Memuat dataset", + "fineTune.status.training": "Melatih", + "fineTune.status.saving": "Menyimpan", + "fineTune.status.completed": "Selesai", + "fineTune.status.failed": "Gagal", + "fineTune.status.stopped": "Dihentikan", + "stop.title": "Hentikan pelatihan?", + "stop.text": "Checkpoint yang disimpan sekarang memungkinkan Anda melanjutkan atau mengekspor apa yang sudah dipelajari sejauh ini.", + "stop.keep": "Hentikan dan simpan checkpoint", + "stop.discard": "Hentikan tanpa checkpoint", + "stop.cancel": "Lanjutkan pelatihan", + "stop.cancelDelete": "Batal", + "chart.loss": "Loss", + "chart.learning_rate": "Learning rate", + "chart.grad_norm": "Norma gradien", + "chart.metric": "Metrik", + "chart.step": "langkah", + "chart.eval": "Loss evaluasi", + "chart.waiting": "Menunggu langkah lainnya.", + "chart.window": "Langkah {{from}} sampai {{to}} dari {{total}}", + "chart.windowEval": "Langkah {{from}} sampai {{to}} dari {{total}}. Titik kosong adalah loss evaluasi.", + "chart.aria": "{{metric}} selama langkah pelatihan. Nilai terakhir adalah {{last}} pada langkah {{step}}.", + "chart.readout": "langkah {{step}}: {{value}}", + "chart.dataTitle": "Tampilkan angkanya", + "chart.emptyText": "Grafik digambar setelah job melaporkan dua langkah.", + "log.title": "Log", + "log.note": "Yang dilaporkan job sejak halaman ini dibuka.", + "log.only": "Hanya peringatan dan error", + "log.copy": "Salin", + "log.copied": "Disalin", + "log.download": "Unduh", + "log.empty": "Belum ada yang dilaporkan.", + "log.noneFiltered": "Tidak ada peringatan atau error.", + "checkpoints.title": "Checkpoint", + "checkpoints.step": "Langkah", + "checkpoints.epoch": "Epoch", + "checkpoints.loss": "Loss", + "checkpoints.path": "Path", + "checkpoints.actions": "Aksi", + "checkpoints.resume": "Lanjutkan", + "checkpoints.resumeTitle": "Lanjutkan pelatihan dari checkpoint ini", + "checkpoints.export": "Ekspor", + "checkpoints.exportTitle": "Ekspor checkpoint ini", + "export.blocked": "Ekspor dimatikan di server ini karena server tidak berbagi folder model dengan host add-on.", + "export.title": "Ekspor sebagai model", + "export.lede": "Ekspor mengubah hasilnya menjadi model yang dapat dimuat LocalAI.", + "export.checkpoint": "Checkpoint", + "export.final": "Model akhir (direktori output)", + "export.checkpointOption": "Langkah {{step}} (loss {{loss}})", + "export.format": "Format ekspor", + "export.lora": "Adapter LoRA", + "export.merged16": "Digabung (16-bit)", + "export.merged4": "Digabung (4-bit)", + "export.quant": "Kuantisasi", + "export.name": "Nama model", + "export.nameHint": "Kosongkan untuk membuat nama dari job.", + "export.go": "Ekspor", + "export.exporting": "Mengekspor...", + "export.working": "Ekspor sedang berjalan...", + "export.done": "Model diekspor dan didaftarkan sebagai \"{{name}}\"", + "export.failed": "Ekspor gagal: {{message}}", + "export.unknown": "error tidak diketahui", + "export.nextTitle": "{{name}} sekarang adalah sebuah model", + "export.nextText": "Model sudah terdaftar di LocalAI. Buka chat dengannya, atau temukan di Model.", + "export.chat": "Chat dengan {{name}}", + "export.models": "Buka di Model", + "export.archive": "Unduh arsip", + "jobs.title": "Job sebelumnya", + "jobs.count_one": "{{count}} disimpan di server ini", + "jobs.count_other": "{{count}} disimpan di server ini", + "jobs.loading": "Memuat job", + "jobs.emptyTitle": "Belum ada job fine-tuning", + "jobs.emptyText": "Siapkan satu di atas. Job akan muncul di sini dengan statusnya, dan Anda dapat menggunakan kembali pengaturannya nanti.", + "jobs.model": "Model", + "jobs.kind": "Jenis", + "jobs.status": "Status", + "jobs.started": "Dimulai", + "jobs.actions": "Aksi", + "jobs.open": "Buka job ini", + "jobs.reuse": "Gunakan ulang", + "jobs.reuseTitle": "Gunakan pengaturan job ini untuk job baru", + "jobs.delete": "Hapus", + "jobs.deleteTitle": "Hapus job ini beserta datanya", + "jobs.deleteDialogTitle": "Hapus job ini?", + "jobs.deleteDialogText": "Ini menghapus job beserta checkpoint dan model hasil ekspor. Tindakan ini tidak dapat dibatalkan.", + "off.badge": "Tidak tersedia untuk Anda", + "off.fineTune.title": "Akun Anda tidak dapat melakukan fine-tuning", + "off.fineTune.text": "Fine-tuning adalah izin tersendiri, dan akun ini tidak memilikinya.", + "off.quantize.title": "Akun Anda tidak dapat melakukan kuantisasi", + "off.quantize.text": "Kuantisasi adalah izin tersendiri, dan akun ini tidak memilikinya.", + "off.admin": "Administrator dapat mengaktifkannya di Pengguna dan kunci.", + "off.askAdmin": "Minta administrator untuk mengaktifkannya untuk akun Anda.", + "off.back": "Ke Beranda", + "landing.experimental": "Eksperimental", + "run.unknownPercent": "Kemajuan belum dilaporkan", + "export.adminOnly.title": "Mengekspor model hanya untuk administrator", + "export.adminOnly.text": "Ekspor menambahkan model ke server ini untuk semua orang yang dapat menggunakannya. Minta administrator untuk mengekspor tugas ini." +} diff --git a/addons/fine-tuning/ui/src/i18n/fine-tuning.it.json b/addons/fine-tuning/ui/src/i18n/fine-tuning.it.json new file mode 100644 index 0000000000..042e46491c --- /dev/null +++ b/addons/fine-tuning/ui/src/i18n/fine-tuning.it.json @@ -0,0 +1,295 @@ +{ + "steps.label": "Avanzamento", + "steps.setup": "Configura", + "steps.check": "Verifica", + "steps.run": "Esecuzione", + "steps.result": "Risultato", + "run.stop": "Ferma", + "run.connecting": "Connessione al job. In attesa del primo evento di avanzamento.", + "run.progress": "Avanzamento", + "run.stages": "Fasi", + "run.step": "passo", + "run.of": "di", + "run.epoch": "epoca", + "run.tokens": "token/s", + "run.eta": "circa {{time}} rimanenti", + "run.etaSeconds": "circa {{seconds}} s rimanenti", + "checks.heading": "Verifica prima di iniziare", + "checks.live": "Si aggiorna mentre cambi la configurazione.", + "checks.meterLabel": "{{used}} per cento in uso, {{free}} liberi di {{total}}", + "checks.openBackends": "Apri Backend", + "checks.barOk": "Nulla blocca l'avvio.", + "checks.barWarn_one": "{{count}} avviso. Puoi comunque avviare.", + "checks.barWarn_other": "{{count}} avvisi. Puoi comunque avviare.", + "checks.title.model": "Modello", + "checks.title.data": "Dataset", + "checks.title.backend": "Backend", + "checks.title.memory": "Memoria", + "checks.title.disk": "Disco", + "checks.title.reward": "Funzioni di ricompensa", + "checks.title.resume": "Ripresa", + "checks.title.access": "Accesso al modello", + "checks.title.type": "Tipo di quantizzazione", + "checks.title.source": "Sorgente", + "checks.title.name": "Nome", + "checks.fineTune.modelOk": "Addestramento di {{model}}.", + "checks.fineTune.modelMissing": "Inserisci il modello da addestrare.", + "checks.fineTune.dataOk": "I dati arrivano da {{source}}.", + "checks.fineTune.dataMissing": "Aggiungi un dataset: un id Hugging Face o un file caricato.", + "checks.fineTune.backendOk": "{{name}} è installato.", + "checks.fineTune.backendMissing": "Nessun backend di fine-tuning è installato, quindi il job potrebbe non partire.", + "checks.fineTune.backendOther": "{{name}} non è tra i backend di fine-tuning installati ({{list}}).", + "checks.fineTune.backendUnknown": "Impossibile leggere i backend installati.", + "checks.fineTune.memoryGpu": "{{name}}: {{free}} liberi di {{total}}. Batch size {{batch}}, lunghezza della sequenza {{seq}}, accumulo del gradiente {{accum}}. Quanta memoria serva al job dipende dal modello e da queste impostazioni, e LocalAI non la stima prima dell'avvio del job.", + "checks.fineTune.memoryCluster": "{{total}} sul nodo migliore ({{node}}). Batch size {{batch}}, lunghezza della sequenza {{seq}}. Quanta memoria serva al job dipende dal modello e da queste impostazioni.", + "checks.fineTune.memoryCpu": "Nessuna GPU trovata. L'addestramento usa la CPU e {{free}} di RAM su {{total}} sono liberi. Aspettati lentezza. Batch size {{batch}}, lunghezza della sequenza {{seq}}.", + "checks.fineTune.memoryUnknown": "Questo account non può leggere la memoria della macchina. Batch size {{batch}}, lunghezza della sequenza {{seq}}. Quanta memoria serva al job dipende dal modello e da queste impostazioni.", + "checks.fineTune.disk": "{{free}} liberi sul disco dei modelli. Download e checkpoint richiedono spazio.", + "checks.fineTune.diskLow": "Solo {{free}} liberi sul disco dei modelli. Download e checkpoint richiedono spazio.", + "checks.fineTune.rewardMissing": "GRPO richiede almeno una funzione di ricompensa. Aggiungine una in Altre opzioni.", + "checks.fineTune.resume": "L'addestramento continua da {{path}}.", + "checks.fineTune.accessNone": "Nessun token impostato. Un modello con accesso limitato richiede un token Hugging Face in Altre opzioni.", + "checks.fineTune.accessSet": "È impostato un token Hugging Face.", + "checks.quantize.modelOk": "Quantizzazione di {{model}}.", + "checks.quantize.modelMissing": "Inserisci il modello da quantizzare.", + "checks.quantize.typeMissing": "Inserisci il tipo di quantizzazione personalizzato.", + "checks.quantize.backendOk": "{{name}} è installato.", + "checks.quantize.backendMissing": "Nessun backend di quantizzazione è installato, quindi il job potrebbe non partire.", + "checks.quantize.backendOther": "{{name}} non è tra i backend di quantizzazione installati ({{list}}).", + "checks.quantize.backendUnknown": "Impossibile leggere i backend installati.", + "checks.quantize.memory": "{{free}} di RAM su {{total}} sono liberi. La conversione gira sulla CPU.", + "checks.quantize.disk": "{{free}} liberi sul disco dei modelli. Download, file convertito e risultato vengono scritti su disco.", + "checks.quantize.diskLow": "Solo {{free}} liberi sul disco dei modelli. Download, file convertito e risultato vengono scritti su disco.", + "checks.quantize.accessNone": "Nessun token impostato. Un modello con accesso limitato richiede un token Hugging Face in Altre opzioni.", + "checks.quantize.accessSet": "È impostato un token Hugging Face.", + "checks.import.sourceOk_huggingface": "Riconosciuto come repository Hugging Face. LocalAI controlla i suoi file quando importi.", + "checks.import.sourceOk_url": "Formato URL riconosciuto. Il contenuto non è stato verificato.", + "checks.import.sourceOk_config": "Riconosciuto come file di configurazione del modello. Viene letto e usato così com'è.", + "checks.import.sourceOk_oci": "Riconosciuto come riferimento a un'immagine OCI.", + "checks.import.sourceOk_ollama": "Riconosciuto come riferimento a un modello Ollama.", + "checks.import.sourceOk_local": "Riconosciuto come percorso sulla macchina che esegue LocalAI.", + "checks.import.sourceOk": "La sorgente è riconosciuta.", + "checks.import.sourceUnknown": "Non sembra un formato noto a LocalAI. Ci proverà comunque, e potrebbe non riuscire.", + "checks.import.backendDownload": "{{name}} non è ancora installato. LocalAI lo scarica prima.", + "checks.import.backendChosen": "{{name}} è scelto e installato.", + "checks.import.backendAuto": "LocalAI sceglie il backend dai file della sorgente quando l'importazione inizia.", + "checks.import.nameTaken": "Un modello chiamato {{name}} è già installato.", + "checks.import.nameFree": "{{name}} non è in uso.", + "checks.import.nameAuto": "Il modello prende il nome dalla sorgente.", + "checks.import.disk": "{{free}} liberi sul disco dei modelli. La dimensione del download è nota quando l'importazione inizia.", + "checks.import.memory_gpu": "{{free}} di memoria GPU sono liberi ora. Quanta memoria serva a questo modello è noto quando l'importazione inizia.", + "checks.import.memory_ram": "{{free}} di RAM sono liberi ora. Quanta memoria serva a questo modello è noto quando l'importazione inizia.", + "checks.import.memory_cluster": "{{free}} sono disponibili sul nodo migliore. Quanta memoria serva a questo modello è noto quando l'importazione inizia.", + "checks.import.memory": "{{free}} di memoria sono liberi ora. Quanta memoria serva a questo modello è noto quando l'importazione inizia.", + "form.key": "Chiave", + "form.value": "Valore", + "form.addOption": "Aggiungi opzione", + "form.copy": "Copia negli appunti", + "form.more": "Altre opzioni", + "form.moreHint": "metodo, backend, adattatore, ottimizzatore, valutazione, funzioni di ricompensa", + "form.baseModel": "Modello base", + "form.model": "Modello", + "form.modelHint": "Un id Hugging Face, o un percorso su questo host.", + "form.data": "I tuoi dati", + "form.dataset": "Sorgente del dataset", + "form.datasetHint": "Un id di dataset Hugging Face, oppure carica un file.", + "form.split": "Suddivisione", + "form.upload": "Carica invece un file", + "form.uploadHint": ".json, .jsonl o .csv", + "form.removeFile": "Rimuovi il file", + "form.train": "Cosa addestrare", + "form.kind": "Tipo di addestramento", + "form.kinds.adapter.title": "Adattatore", + "form.kinds.adapter.text": "Addestra un piccolo adattatore e lascia invariati i pesi base. È la scelta abituale, e richiede poca memoria.", + "form.kinds.full.title": "Modello completo", + "form.kinds.full.text": "Cambia ogni peso. Richiede diverse volte la memoria di un adattatore.", + "form.epochs": "Epoche", + "form.batch": "Batch size", + "form.lr": "Tasso di apprendimento", + "form.backend": "Backend", + "form.method": "Metodo di addestramento", + "form.adapterKind": "Tipo di adattatore", + "form.rank": "Rank", + "form.alpha": "Alpha", + "form.dropout": "Dropout", + "form.gradAccum": "Passi di accumulo del gradiente", + "form.warmup": "Passi di warmup", + "form.seqLen": "Lunghezza massima della sequenza", + "form.optimizer": "Ottimizzatore", + "form.maxSteps": "Passi massimi", + "form.maxStepsHint": "0 per automatico.", + "form.saveSteps": "Passi di salvataggio", + "form.saveLimit": "Limite totale di salvataggi", + "form.saveLimitHint": "0 mantiene ogni checkpoint.", + "form.weightDecay": "Weight decay", + "form.seed": "Seed", + "form.seedHint": "0 sceglie un seed casuale.", + "form.precision": "Precisione mista", + "form.auto": "Automatico", + "form.targetModules": "Moduli target", + "form.targetModulesHint": "Separati da virgola. Lascia vuoto per il valore predefinito del backend.", + "form.gradCheckpointing": "Gradient checkpointing (più lento, usa meno memoria)", + "form.token": "Token Hugging Face", + "form.tokenHint": "Serve solo per i modelli con accesso limitato.", + "form.evalEnable": "Valuta durante l'addestramento", + "form.evalStrategy": "Strategia di valutazione", + "form.evalSteps_": "Passi", + "form.evalEpoch": "Epoca", + "form.evalSteps": "Passi di valutazione", + "form.evalStepsHint": "0 coincide con i passi di salvataggio.", + "form.evalSplit": "Suddivisione per la valutazione", + "form.evalDataset": "Dataset di valutazione", + "form.evalDatasetPlaceholder": "Un dataset separato", + "form.evalRatio": "Rapporto di suddivisione automatica", + "form.rewards": "Funzioni di ricompensa (GRPO)", + "form.rewardsHint": "GRPO richiede almeno una funzione di ricompensa. Scegline una integrata o aggiungi la tua.", + "form.resume": "Riprendi dal checkpoint", + "form.clearCheckpoint": "Cancella il checkpoint", + "form.extra": "Opzioni extra", + "form.extraHint": "Coppie chiave e valore specifiche del backend.", + "form.summaryFull": "modello completo", + "form.summary_lora": "adattatore LoRA", + "form.summary_loha": "adattatore LoHa", + "form.summary_lokr": "adattatore LoKr", + "form.recipe": "Addestra un {{kind}} di {{model}} con {{method}}, {{epochs}} epoche sui dati {{data}}.", + "form.recipeEmpty": "Scegli un modello e un dataset per vedere cosa verrà addestrato.", + "rewards.format_reward": "Verifica il formato ... seguito dalla risposta.", + "rewards.reasoning_accuracy_reward": "Confronta il contenuto di con la colonna delle risposte del dataset.", + "rewards.length_reward": "Valuta quanto la risposta è vicina a una lunghezza target.", + "rewards.xml_tag_reward": "Valuta i tag XML aperti e chiusi correttamente.", + "rewards.no_repetition_reward": "Penalizza gli n-gram ripetuti.", + "rewards.code_execution_reward": "Verifica che i blocchi di codice Python abbiano una sintassi valida.", + "rewards.param_target_length": "Lunghezza target", + "rewards.fnName": "Nome della funzione", + "rewards.fnBody": "Corpo della funzione", + "rewards.fnHint": "Riceve completions e kwargs e deve restituire una lista di float. Sono disponibili re, math, json e string.", + "rewards.add": "Aggiungi", + "rewards.cancel": "Annulla", + "rewards.custom": "Aggiungi una funzione di ricompensa personalizzata", + "fineTune.title": "Fine-tuning", + "fineTune.lede": "Addestra un adattatore sui tuoi dati, poi esportalo come modello con cui puoi chattare.", + "fineTune.importConfig": "Importa configurazione", + "fineTune.exportConfig": "Esporta configurazione", + "fineTune.newJob": "Nuovo job", + "fineTune.badConfig": "Impossibile leggere il file. Usa una configurazione JSON esportata da questa pagina.", + "fineTune.start": "Avvia il fine-tuning", + "fineTune.starting": "Avvio in corso...", + "fineTune.resume": "Riprendi l'addestramento", + "fineTune.resuming": "Ripresa dal checkpoint:", + "fineTune.clear": "Cancella", + "fineTune.barNote": "Non cambia nulla finché non avvii.", + "fineTune.jobMeta": "{{kind}} {{method}} su {{backend}}, avviato {{started}}", + "fineTune.failed": "Addestramento non riuscito", + "fineTune.failedAt": "Addestramento fermato al passo {{step}} di {{total}}", + "fineTune.noMessage": "Il backend non ha dato alcun messaggio.", + "fineTune.finished": "Addestramento completato.", + "fineTune.finishedLoss": "Addestramento completato. L'ultima loss è stata {{loss}}.", + "fineTune.stopped": "Il job è stato fermato. Un checkpoint viene conservato se hai scelto di farlo.", + "fineTune.tryTitle": "Cosa fare dopo", + "fineTune.tryText": "Il server ha segnalato che la memoria è esaurita. Ogni modifica qui sotto viene applicata a una copia della configurazione, e nulla parte finché non premi Avvia.", + "fineTune.tryPlain": "Apri di nuovo la configurazione, cambia ciò che indica il messaggio e avvia un nuovo job.", + "fineTune.fixBatch": "Riduci il batch size a 1", + "fineTune.fixCheckpointing": "Attiva il gradient checkpointing (più lento, usa meno memoria)", + "fineTune.retryChanges": "Apri la configurazione con queste modifiche", + "fineTune.reuse": "Apri di nuovo la configurazione", + "fineTune.resumeFrom": "Riprendi dal passo {{step}}", + "fineTune.status.queued": "In coda", + "fineTune.status.loading_model": "Caricamento del modello", + "fineTune.status.loading_dataset": "Caricamento del dataset", + "fineTune.status.training": "Addestramento", + "fineTune.status.saving": "Salvataggio", + "fineTune.status.completed": "Completato", + "fineTune.status.failed": "Non riuscito", + "fineTune.status.stopped": "Fermato", + "stop.title": "Fermare l'addestramento?", + "stop.text": "Un checkpoint salvato ora ti permette di riprendere o esportare ciò che è stato appreso finora.", + "stop.keep": "Ferma e conserva un checkpoint", + "stop.discard": "Ferma senza checkpoint", + "stop.cancel": "Continua l'addestramento", + "stop.cancelDelete": "Annulla", + "chart.loss": "Loss", + "chart.learning_rate": "Tasso di apprendimento", + "chart.grad_norm": "Norma del gradiente", + "chart.metric": "Metrica", + "chart.step": "passo", + "chart.eval": "Loss di valutazione", + "chart.waiting": "In attesa di altri passi.", + "chart.window": "Passi da {{from}} a {{to}} di {{total}}", + "chart.windowEval": "Passi da {{from}} a {{to}} di {{total}}. I punti vuoti sono la loss di valutazione.", + "chart.aria": "{{metric}} lungo i passi di addestramento. L'ultimo valore è {{last}} al passo {{step}}.", + "chart.readout": "passo {{step}}: {{value}}", + "chart.dataTitle": "Mostra i numeri", + "chart.emptyText": "Il grafico si disegna quando il job ha riportato due passi.", + "log.title": "Log", + "log.note": "Ciò che il job ha riportato da quando è aperta questa pagina.", + "log.only": "Solo avvisi ed errori", + "log.copy": "Copia", + "log.copied": "Copiato", + "log.download": "Scarica", + "log.empty": "Ancora nulla da riportare.", + "log.noneFiltered": "Nessun avviso o errore.", + "checkpoints.title": "Checkpoint", + "checkpoints.step": "Passo", + "checkpoints.epoch": "Epoca", + "checkpoints.loss": "Loss", + "checkpoints.path": "Percorso", + "checkpoints.actions": "Azioni", + "checkpoints.resume": "Riprendi", + "checkpoints.resumeTitle": "Riprendi l'addestramento da questo checkpoint", + "checkpoints.export": "Esporta", + "checkpoints.exportTitle": "Esporta questo checkpoint", + "export.blocked": "L'esportazione è disattivata su questo server perché non condivide la cartella dei modelli con l'host dell'add-on.", + "export.title": "Esporta come modello", + "export.lede": "L'esportazione trasforma il risultato in un modello che LocalAI può caricare.", + "export.checkpoint": "Checkpoint", + "export.final": "Modello finale (cartella di output)", + "export.checkpointOption": "Passo {{step}} (loss {{loss}})", + "export.format": "Formato di esportazione", + "export.lora": "Adattatore LoRA", + "export.merged16": "Unito (16 bit)", + "export.merged4": "Unito (4 bit)", + "export.quant": "Quantizzazione", + "export.name": "Nome del modello", + "export.nameHint": "Lascia vuoto per generarlo dal job.", + "export.go": "Esporta", + "export.exporting": "Esportazione...", + "export.working": "Esportazione in corso...", + "export.done": "Modello esportato e registrato come \"{{name}}\"", + "export.failed": "Esportazione non riuscita: {{message}}", + "export.unknown": "errore sconosciuto", + "export.nextTitle": "{{name}} è ora un modello", + "export.nextText": "È registrato in LocalAI. Apri una chat con esso, oppure trovalo in Modelli.", + "export.chat": "Chatta con {{name}}", + "export.models": "Apri in Modelli", + "export.archive": "Scarica l'archivio", + "jobs.title": "Job precedenti", + "jobs.count_one": "{{count}} conservato su questo server", + "jobs.count_other": "{{count}} conservati su questo server", + "jobs.loading": "Caricamento dei job", + "jobs.emptyTitle": "Ancora nessun job di fine-tuning", + "jobs.emptyText": "Configurane uno sopra. Compare qui con il suo stato, e in seguito puoi riusarne la configurazione.", + "jobs.model": "Modello", + "jobs.kind": "Tipo", + "jobs.status": "Stato", + "jobs.started": "Avviato", + "jobs.actions": "Azioni", + "jobs.open": "Apri questo job", + "jobs.reuse": "Riusa", + "jobs.reuseTitle": "Usa la configurazione di questo job per un nuovo job", + "jobs.delete": "Elimina", + "jobs.deleteTitle": "Elimina questo job e i suoi dati", + "jobs.deleteDialogTitle": "Eliminare questo job?", + "jobs.deleteDialogText": "Questo elimina il job con i suoi checkpoint e l'eventuale modello esportato. Non può essere annullato.", + "off.badge": "Non disponibile per te", + "off.fineTune.title": "Il tuo account non può fare fine-tuning", + "off.fineTune.text": "Il fine-tuning è un permesso separato, e questo account non ce l'ha.", + "off.quantize.title": "Il tuo account non può quantizzare", + "off.quantize.text": "La quantizzazione è un permesso separato, e questo account non ce l'ha.", + "off.admin": "Un amministratore può attivarlo da Utenti e chiavi.", + "off.askAdmin": "Chiedi a un amministratore di attivarlo per il tuo account.", + "off.back": "Vai alla Home", + "landing.experimental": "Sperimentale", + "run.unknownPercent": "Avanzamento non ancora comunicato", + "export.adminOnly.title": "L'esportazione di un modello è riservata agli amministratori", + "export.adminOnly.text": "L'esportazione aggiunge un modello a questo server per tutti quelli che possono usarlo. Chiedi a un amministratore di esportare questo lavoro." +} diff --git a/addons/fine-tuning/ui/src/i18n/fine-tuning.ko.json b/addons/fine-tuning/ui/src/i18n/fine-tuning.ko.json new file mode 100644 index 0000000000..18d02e6398 --- /dev/null +++ b/addons/fine-tuning/ui/src/i18n/fine-tuning.ko.json @@ -0,0 +1,295 @@ +{ + "steps.label": "진행 상황", + "steps.setup": "설정", + "steps.check": "확인", + "steps.run": "실행", + "steps.result": "결과", + "run.stop": "중지", + "run.connecting": "작업에 연결하는 중입니다. 첫 진행 이벤트를 기다리고 있습니다.", + "run.progress": "진행률", + "run.stages": "단계", + "run.step": "스텝", + "run.of": "/", + "run.epoch": "에포크", + "run.tokens": "tokens/s", + "run.eta": "약 {{time}} 남음", + "run.etaSeconds": "약 {{seconds}}초 남음", + "checks.heading": "시작하기 전에 확인", + "checks.live": "설정을 바꿀 때마다 업데이트됩니다.", + "checks.meterLabel": "{{used}}% 사용 중, {{total}} 중 {{free}} 사용 가능", + "checks.openBackends": "백엔드 열기", + "checks.barOk": "시작을 막는 항목이 없습니다.", + "checks.barWarn_one": "경고 {{count}}개. 그래도 시작할 수 있습니다.", + "checks.barWarn_other": "경고 {{count}}개. 그래도 시작할 수 있습니다.", + "checks.title.model": "모델", + "checks.title.data": "데이터셋", + "checks.title.backend": "백엔드", + "checks.title.memory": "메모리", + "checks.title.disk": "디스크", + "checks.title.reward": "보상 함수", + "checks.title.resume": "재개", + "checks.title.access": "모델 접근", + "checks.title.type": "양자화 유형", + "checks.title.source": "소스", + "checks.title.name": "이름", + "checks.fineTune.modelOk": "{{model}} 학습 중입니다.", + "checks.fineTune.modelMissing": "학습할 모델을 입력하세요.", + "checks.fineTune.dataOk": "데이터는 {{source}}에서 옵니다.", + "checks.fineTune.dataMissing": "데이터셋을 추가하세요: Hugging Face id 또는 업로드한 파일.", + "checks.fineTune.backendOk": "{{name}}이(가) 설치되어 있습니다.", + "checks.fineTune.backendMissing": "파인튜닝 백엔드가 설치되어 있지 않아 작업이 시작되지 않을 수 있습니다.", + "checks.fineTune.backendOther": "{{name}}은(는) 설치된 파인튜닝 백엔드({{list}}) 중에 없습니다.", + "checks.fineTune.backendUnknown": "설치된 백엔드를 읽을 수 없습니다.", + "checks.fineTune.memoryGpu": "{{name}}: {{total}} 중 {{free}} 사용 가능. 배치 크기 {{batch}}, 시퀀스 길이 {{seq}}, 그래디언트 누적 {{accum}}. 작업에 필요한 양은 모델과 이 설정에 따라 다르며, LocalAI는 작업 시작 전에 이를 추정하지 않습니다.", + "checks.fineTune.memoryCluster": "최적 노드({{node}})의 {{total}}. 배치 크기 {{batch}}, 시퀀스 길이 {{seq}}. 작업에 필요한 양은 모델과 이 설정에 따라 다릅니다.", + "checks.fineTune.memoryCpu": "GPU를 찾을 수 없습니다. 학습은 CPU를 사용하며 RAM {{total}} 중 {{free}}가 사용 가능합니다. 느릴 것으로 예상됩니다. 배치 크기 {{batch}}, 시퀀스 길이 {{seq}}.", + "checks.fineTune.memoryUnknown": "이 계정은 컴퓨터의 메모리를 읽을 수 없습니다. 배치 크기 {{batch}}, 시퀀스 길이 {{seq}}. 작업에 필요한 양은 모델과 이 설정에 따라 다릅니다.", + "checks.fineTune.disk": "모델 디스크에 {{free}} 사용 가능. 다운로드와 체크포인트에 공간이 필요합니다.", + "checks.fineTune.diskLow": "모델 디스크에 {{free}}만 사용 가능. 다운로드와 체크포인트에 공간이 필요합니다.", + "checks.fineTune.rewardMissing": "GRPO에는 보상 함수가 최소 하나 필요합니다. 추가 옵션에서 하나를 추가하세요.", + "checks.fineTune.resume": "학습이 {{path}}에서 이어집니다.", + "checks.fineTune.accessNone": "토큰이 설정되지 않았습니다. 접근이 제한된 모델은 추가 옵션에서 Hugging Face 토큰이 필요합니다.", + "checks.fineTune.accessSet": "Hugging Face 토큰이 설정되어 있습니다.", + "checks.quantize.modelOk": "{{model}}을(를) 양자화합니다.", + "checks.quantize.modelMissing": "양자화할 모델을 입력하세요.", + "checks.quantize.typeMissing": "사용자 지정 양자화 유형을 입력하세요.", + "checks.quantize.backendOk": "{{name}}이(가) 설치되어 있습니다.", + "checks.quantize.backendMissing": "양자화 백엔드가 설치되어 있지 않아 작업이 시작되지 않을 수 있습니다.", + "checks.quantize.backendOther": "{{name}}은(는) 설치된 양자화 백엔드({{list}}) 중에 없습니다.", + "checks.quantize.backendUnknown": "설치된 백엔드를 읽을 수 없습니다.", + "checks.quantize.memory": "RAM {{total}} 중 {{free}}가 사용 가능합니다. 변환은 CPU에서 실행됩니다.", + "checks.quantize.disk": "모델 디스크에 {{free}} 사용 가능. 다운로드, 변환된 파일, 결과가 디스크에 기록됩니다.", + "checks.quantize.diskLow": "모델 디스크에 {{free}}만 사용 가능. 다운로드, 변환된 파일, 결과가 디스크에 기록됩니다.", + "checks.quantize.accessNone": "토큰이 설정되지 않았습니다. 접근이 제한된 모델은 추가 옵션에서 Hugging Face 토큰이 필요합니다.", + "checks.quantize.accessSet": "Hugging Face 토큰이 설정되어 있습니다.", + "checks.import.sourceOk_huggingface": "Hugging Face 저장소로 인식되었습니다. 가져올 때 LocalAI가 파일을 확인합니다.", + "checks.import.sourceOk_url": "URL 형식이 인식되었습니다. 내용은 확인하지 않았습니다.", + "checks.import.sourceOk_config": "모델 구성 파일로 인식되었습니다. 읽은 뒤 있는 그대로 사용합니다.", + "checks.import.sourceOk_oci": "OCI 이미지 참조로 인식되었습니다.", + "checks.import.sourceOk_ollama": "Ollama 모델 참조로 인식되었습니다.", + "checks.import.sourceOk_local": "LocalAI를 실행하는 컴퓨터의 경로로 인식되었습니다.", + "checks.import.sourceOk": "소스가 인식되었습니다.", + "checks.import.sourceUnknown": "LocalAI가 아는 형식이 아닌 것 같습니다. 그래도 시도하며, 실패할 수 있습니다.", + "checks.import.backendDownload": "{{name}}이(가) 아직 설치되어 있지 않습니다. LocalAI가 먼저 다운로드합니다.", + "checks.import.backendChosen": "{{name}}이(가) 선택되어 설치되어 있습니다.", + "checks.import.backendAuto": "가져오기가 시작되면 LocalAI가 소스의 파일을 보고 백엔드를 고릅니다.", + "checks.import.nameTaken": "{{name}}이라는 모델이 이미 설치되어 있습니다.", + "checks.import.nameFree": "{{name}}은(는) 사용 중이 아닙니다.", + "checks.import.nameAuto": "모델은 소스 이름을 따라 명명됩니다.", + "checks.import.disk": "모델 디스크에 {{free}} 사용 가능. 다운로드 크기는 가져오기가 시작되면 알 수 있습니다.", + "checks.import.memory_gpu": "지금 GPU 메모리 {{free}}이(가) 사용 가능합니다. 이 모델에 필요한 양은 가져오기가 시작되면 알 수 있습니다.", + "checks.import.memory_ram": "지금 RAM {{free}}이(가) 사용 가능합니다. 이 모델에 필요한 양은 가져오기가 시작되면 알 수 있습니다.", + "checks.import.memory_cluster": "최적 노드에 {{free}}이(가) 사용 가능합니다. 이 모델에 필요한 양은 가져오기가 시작되면 알 수 있습니다.", + "checks.import.memory": "지금 메모리 {{free}}이(가) 사용 가능합니다. 이 모델에 필요한 양은 가져오기가 시작되면 알 수 있습니다.", + "form.key": "키", + "form.value": "값", + "form.addOption": "옵션 추가", + "form.copy": "클립보드에 복사", + "form.more": "추가 옵션", + "form.moreHint": "방법, 백엔드, 어댑터, 옵티마이저, 평가, 보상 함수", + "form.baseModel": "기본 모델", + "form.model": "모델", + "form.modelHint": "Hugging Face id 또는 이 호스트의 경로.", + "form.data": "내 데이터", + "form.dataset": "데이터셋 소스", + "form.datasetHint": "Hugging Face 데이터셋 id 또는 파일 업로드.", + "form.split": "분할", + "form.upload": "대신 파일 업로드", + "form.uploadHint": ".json, .jsonl 또는 .csv", + "form.removeFile": "파일 제거", + "form.train": "학습 대상", + "form.kind": "학습 종류", + "form.kinds.adapter.title": "어댑터", + "form.kinds.adapter.text": "작은 어댑터를 학습하고 기본 가중치는 건드리지 않습니다. 보통 이 방식을 선택하며 메모리가 적게 듭니다.", + "form.kinds.full.title": "전체 모델", + "form.kinds.full.text": "모든 가중치를 변경합니다. 어댑터보다 여러 배의 메모리가 필요합니다.", + "form.epochs": "에포크", + "form.batch": "배치 크기", + "form.lr": "학습률", + "form.backend": "백엔드", + "form.method": "학습 방법", + "form.adapterKind": "어댑터 종류", + "form.rank": "랭크", + "form.alpha": "알파", + "form.dropout": "드롭아웃", + "form.gradAccum": "그래디언트 누적 스텝", + "form.warmup": "워밍업 스텝", + "form.seqLen": "최대 시퀀스 길이", + "form.optimizer": "옵티마이저", + "form.maxSteps": "최대 스텝", + "form.maxStepsHint": "0이면 자동입니다.", + "form.saveSteps": "저장 스텝", + "form.saveLimit": "총 저장 한도", + "form.saveLimitHint": "0이면 모든 체크포인트를 유지합니다.", + "form.weightDecay": "가중치 감쇠", + "form.seed": "시드", + "form.seedHint": "0이면 무작위 시드를 고릅니다.", + "form.precision": "혼합 정밀도", + "form.auto": "자동", + "form.targetModules": "대상 모듈", + "form.targetModulesHint": "쉼표로 구분합니다. 비워 두면 백엔드 기본값을 사용합니다.", + "form.gradCheckpointing": "그래디언트 체크포인팅 (느리지만 메모리를 적게 사용)", + "form.token": "Hugging Face 토큰", + "form.tokenHint": "접근이 제한된 모델에만 필요합니다.", + "form.evalEnable": "학습 중 평가", + "form.evalStrategy": "평가 전략", + "form.evalSteps_": "스텝", + "form.evalEpoch": "에포크", + "form.evalSteps": "평가 스텝", + "form.evalStepsHint": "0이면 저장 스텝과 같게 설정됩니다.", + "form.evalSplit": "평가 분할", + "form.evalDataset": "평가 데이터셋", + "form.evalDatasetPlaceholder": "별도 데이터셋", + "form.evalRatio": "자동 분할 비율", + "form.rewards": "보상 함수 (GRPO)", + "form.rewardsHint": "GRPO에는 보상 함수가 최소 하나 필요합니다. 기본 제공 함수를 고르거나 직접 추가하세요.", + "form.resume": "체크포인트에서 재개", + "form.clearCheckpoint": "체크포인트 지우기", + "form.extra": "추가 옵션", + "form.extraHint": "백엔드별 키와 값 쌍입니다.", + "form.summaryFull": "전체 모델", + "form.summary_lora": "LoRA 어댑터", + "form.summary_loha": "LoHa 어댑터", + "form.summary_lokr": "LoKr 어댑터", + "form.recipe": "{{data}}를 사용해 {{method}}로 {{model}}의 {{kind}}를 {{epochs}} 에포크 학습합니다.", + "form.recipeEmpty": "모델과 데이터셋을 선택하면 학습 내용을 볼 수 있습니다.", + "rewards.format_reward": "... 형식과 그 다음 답변 형식을 확인합니다.", + "rewards.reasoning_accuracy_reward": " 내용을 데이터셋의 답변 열과 비교합니다.", + "rewards.length_reward": "답변이 목표 길이에 얼마나 가까운지 점수를 매깁니다.", + "rewards.xml_tag_reward": "열고 닫은 XML 태그가 올바른지 점수를 매깁니다.", + "rewards.no_repetition_reward": "반복되는 n-gram에 감점을 줍니다.", + "rewards.code_execution_reward": "Python 코드 블록의 문법이 유효한지 확인합니다.", + "rewards.param_target_length": "목표 길이", + "rewards.fnName": "함수 이름", + "rewards.fnBody": "함수 본문", + "rewards.fnHint": "completions와 kwargs를 받아 float 리스트를 반환해야 합니다. re, math, json, string을 사용할 수 있습니다.", + "rewards.add": "추가", + "rewards.cancel": "취소", + "rewards.custom": "사용자 지정 보상 함수 추가", + "fineTune.title": "파인튜닝", + "fineTune.lede": "내 데이터로 어댑터를 학습한 뒤, 대화할 수 있는 모델로 내보냅니다.", + "fineTune.importConfig": "구성 가져오기", + "fineTune.exportConfig": "구성 내보내기", + "fineTune.newJob": "새 작업", + "fineTune.badConfig": "파일을 읽을 수 없습니다. 이 페이지에서 내보낸 JSON 구성을 사용하세요.", + "fineTune.start": "파인튜닝 시작", + "fineTune.starting": "시작 중...", + "fineTune.resume": "학습 재개", + "fineTune.resuming": "체크포인트에서 재개 중:", + "fineTune.clear": "지우기", + "fineTune.barNote": "시작하기 전까지는 아무것도 바뀌지 않습니다.", + "fineTune.jobMeta": "{{backend}}의 {{kind}} {{method}}, 시작 {{started}}", + "fineTune.failed": "학습 실패", + "fineTune.failedAt": "{{total}} 스텝 중 {{step}} 스텝에서 학습이 중지되었습니다", + "fineTune.noMessage": "백엔드가 메시지를 보내지 않았습니다.", + "fineTune.finished": "학습이 완료되었습니다.", + "fineTune.finishedLoss": "학습이 완료되었습니다. 마지막 손실은 {{loss}}입니다.", + "fineTune.stopped": "작업이 중지되었습니다. 유지하도록 선택한 경우 체크포인트가 보존됩니다.", + "fineTune.tryTitle": "다음에 할 일", + "fineTune.tryText": "서버가 메모리 부족을 보고했습니다. 아래의 각 변경 사항은 설정 사본에 적용되며, 시작을 누르기 전까지 아무것도 실행되지 않습니다.", + "fineTune.tryPlain": "설정을 다시 열고, 메시지가 가리키는 항목을 바꾼 뒤 새 작업을 시작하세요.", + "fineTune.fixBatch": "배치 크기를 1로 낮추기", + "fineTune.fixCheckpointing": "그래디언트 체크포인팅 켜기 (느리지만 메모리를 적게 사용)", + "fineTune.retryChanges": "이 변경 사항을 적용해 설정 열기", + "fineTune.reuse": "설정 다시 열기", + "fineTune.resumeFrom": "{{step}} 스텝부터 재개", + "fineTune.status.queued": "대기 중", + "fineTune.status.loading_model": "모델 불러오는 중", + "fineTune.status.loading_dataset": "데이터셋 불러오는 중", + "fineTune.status.training": "학습 중", + "fineTune.status.saving": "저장 중", + "fineTune.status.completed": "완료됨", + "fineTune.status.failed": "실패", + "fineTune.status.stopped": "중지됨", + "stop.title": "학습을 중지할까요?", + "stop.text": "지금 저장된 체크포인트가 있으면 지금까지 학습된 내용을 재개하거나 내보낼 수 있습니다.", + "stop.keep": "중지하고 체크포인트 유지", + "stop.discard": "체크포인트 없이 중지", + "stop.cancel": "학습 계속하기", + "stop.cancelDelete": "취소", + "chart.loss": "손실", + "chart.learning_rate": "학습률", + "chart.grad_norm": "그래디언트 노름", + "chart.metric": "지표", + "chart.step": "스텝", + "chart.eval": "평가 손실", + "chart.waiting": "더 많은 스텝을 기다리는 중입니다.", + "chart.window": "{{total}} 중 {{from}}~{{to}} 스텝", + "chart.windowEval": "{{total}} 중 {{from}}~{{to}} 스텝. 빈 점은 평가 손실입니다.", + "chart.aria": "학습 스텝에 따른 {{metric}}. 마지막 값은 {{step}} 스텝의 {{last}}입니다.", + "chart.readout": "{{step}} 스텝: {{value}}", + "chart.dataTitle": "숫자 보기", + "chart.emptyText": "작업이 두 스텝을 보고하면 차트가 그려집니다.", + "log.title": "로그", + "log.note": "이 페이지가 열린 뒤 작업이 보고한 내용입니다.", + "log.only": "경고와 오류만", + "log.copy": "복사", + "log.copied": "복사됨", + "log.download": "다운로드", + "log.empty": "아직 보고된 내용이 없습니다.", + "log.noneFiltered": "경고나 오류가 없습니다.", + "checkpoints.title": "체크포인트", + "checkpoints.step": "스텝", + "checkpoints.epoch": "에포크", + "checkpoints.loss": "손실", + "checkpoints.path": "경로", + "checkpoints.actions": "작업", + "checkpoints.resume": "재개", + "checkpoints.resumeTitle": "이 체크포인트에서 학습 재개", + "checkpoints.export": "내보내기", + "checkpoints.exportTitle": "이 체크포인트 내보내기", + "export.blocked": "이 서버는 애드온 호스트와 모델 폴더를 공유하지 않으므로 내보내기가 꺼져 있습니다.", + "export.title": "모델로 내보내기", + "export.lede": "내보내기는 결과를 LocalAI가 불러올 수 있는 모델로 바꿉니다.", + "export.checkpoint": "체크포인트", + "export.final": "최종 모델 (출력 디렉터리)", + "export.checkpointOption": "{{step}} 스텝 (손실 {{loss}})", + "export.format": "내보내기 형식", + "export.lora": "LoRA 어댑터", + "export.merged16": "병합 (16비트)", + "export.merged4": "병합 (4비트)", + "export.quant": "양자화", + "export.name": "모델 이름", + "export.nameHint": "비워 두면 작업으로부터 이름을 생성합니다.", + "export.go": "내보내기", + "export.exporting": "내보내는 중...", + "export.working": "내보내기 진행 중...", + "export.done": "모델을 내보냈고 \"{{name}}\"(으)로 등록했습니다", + "export.failed": "내보내기 실패: {{message}}", + "export.unknown": "알 수 없는 오류", + "export.nextTitle": "{{name}}이(가) 이제 모델입니다", + "export.nextText": "LocalAI에 등록되었습니다. 이 모델과 채팅을 열거나 모델 페이지에서 찾으세요.", + "export.chat": "{{name}}과(와) 채팅", + "export.models": "모델에서 열기", + "export.archive": "아카이브 다운로드", + "jobs.title": "이전 작업", + "jobs.count_one": "이 서버에 {{count}}개 보관됨", + "jobs.count_other": "이 서버에 {{count}}개 보관됨", + "jobs.loading": "작업 불러오는 중", + "jobs.emptyTitle": "아직 파인튜닝 작업이 없습니다", + "jobs.emptyText": "위에서 작업을 설정하세요. 작업은 상태와 함께 여기에 나타나며, 나중에 설정을 재사용할 수 있습니다.", + "jobs.model": "모델", + "jobs.kind": "종류", + "jobs.status": "상태", + "jobs.started": "시작 시간", + "jobs.actions": "작업", + "jobs.open": "이 작업 열기", + "jobs.reuse": "재사용", + "jobs.reuseTitle": "이 작업의 설정을 새 작업에 사용", + "jobs.delete": "삭제", + "jobs.deleteTitle": "이 작업과 데이터 삭제", + "jobs.deleteDialogTitle": "이 작업을 삭제할까요?", + "jobs.deleteDialogText": "작업을 체크포인트와 내보낸 모델과 함께 삭제합니다. 이 작업은 되돌릴 수 없습니다.", + "off.badge": "사용할 수 없음", + "off.fineTune.title": "이 계정은 파인튜닝을 할 수 없습니다", + "off.fineTune.text": "파인튜닝은 별도의 권한이며, 이 계정에는 그 권한이 없습니다.", + "off.quantize.title": "이 계정은 양자화를 할 수 없습니다", + "off.quantize.text": "양자화는 별도의 권한이며, 이 계정에는 그 권한이 없습니다.", + "off.admin": "관리자가 사용자 및 키 메뉴에서 켤 수 있습니다.", + "off.askAdmin": "관리자에게 이 계정에 대해 켜 달라고 요청하세요.", + "off.back": "홈으로 이동", + "landing.experimental": "실험적", + "run.unknownPercent": "진행률이 아직 보고되지 않았습니다", + "export.adminOnly.title": "모델 내보내기는 관리자 전용입니다", + "export.adminOnly.text": "내보내기는 이 서버를 쓸 수 있는 모든 사람을 위해 모델을 추가합니다. 관리자에게 이 작업의 내보내기를 요청하세요." +} diff --git a/addons/fine-tuning/ui/src/i18n/fine-tuning.pt-BR.json b/addons/fine-tuning/ui/src/i18n/fine-tuning.pt-BR.json new file mode 100644 index 0000000000..b3f1183f52 --- /dev/null +++ b/addons/fine-tuning/ui/src/i18n/fine-tuning.pt-BR.json @@ -0,0 +1,295 @@ +{ + "steps.label": "Progresso", + "steps.setup": "Configurar", + "steps.check": "Verificar", + "steps.run": "Executar", + "steps.result": "Resultado", + "run.stop": "Parar", + "run.connecting": "Conectando ao job. Aguardando o primeiro evento de progresso.", + "run.progress": "Progresso", + "run.stages": "Etapas", + "run.step": "passo", + "run.of": "de", + "run.epoch": "época", + "run.tokens": "tokens/s", + "run.eta": "cerca de {{time}} restantes", + "run.etaSeconds": "cerca de {{seconds}} s restantes", + "checks.heading": "Verifique antes de começar", + "checks.live": "Atualiza conforme você muda a configuração.", + "checks.meterLabel": "{{used}} por cento em uso, {{free}} livres de {{total}}", + "checks.openBackends": "Abrir Backends", + "checks.barOk": "Nada impede o início.", + "checks.barWarn_one": "{{count}} aviso. Você ainda pode iniciar.", + "checks.barWarn_other": "{{count}} avisos. Você ainda pode iniciar.", + "checks.title.model": "Modelo", + "checks.title.data": "Dataset", + "checks.title.backend": "Backend", + "checks.title.memory": "Memória", + "checks.title.disk": "Disco", + "checks.title.reward": "Funções de recompensa", + "checks.title.resume": "Retomando", + "checks.title.access": "Acesso ao modelo", + "checks.title.type": "Tipo de quantização", + "checks.title.source": "Origem", + "checks.title.name": "Nome", + "checks.fineTune.modelOk": "Treinando {{model}}.", + "checks.fineTune.modelMissing": "Informe o modelo a treinar.", + "checks.fineTune.dataOk": "Os dados vêm de {{source}}.", + "checks.fineTune.dataMissing": "Adicione um dataset: um id do Hugging Face ou um arquivo enviado.", + "checks.fineTune.backendOk": "{{name}} está instalado.", + "checks.fineTune.backendMissing": "Nenhum backend de ajuste fino está instalado, então o job pode não iniciar.", + "checks.fineTune.backendOther": "{{name}} não está entre os backends de ajuste fino instalados ({{list}}).", + "checks.fineTune.backendUnknown": "Não foi possível ler os backends instalados.", + "checks.fineTune.memoryGpu": "{{name}}: {{free}} de {{total}} livres. Tamanho do lote {{batch}}, comprimento da sequência {{seq}}, acumulação de gradiente {{accum}}. Quanto o job precisa depende do modelo e destas configurações, e o LocalAI não estima isso antes de o job começar.", + "checks.fineTune.memoryCluster": "{{total}} no melhor nó ({{node}}). Tamanho do lote {{batch}}, comprimento da sequência {{seq}}. Quanto o job precisa depende do modelo e destas configurações.", + "checks.fineTune.memoryCpu": "Nenhuma GPU encontrada. O treino usa a CPU e {{free}} de {{total}} de RAM estão livres. Espere que seja lento. Tamanho do lote {{batch}}, comprimento da sequência {{seq}}.", + "checks.fineTune.memoryUnknown": "Esta conta não consegue ler a memória da máquina. Tamanho do lote {{batch}}, comprimento da sequência {{seq}}. Quanto o job precisa depende do modelo e destas configurações.", + "checks.fineTune.disk": "{{free}} livres no disco de modelos. Downloads e checkpoints precisam de espaço.", + "checks.fineTune.diskLow": "Só {{free}} livres no disco de modelos. Downloads e checkpoints precisam de espaço.", + "checks.fineTune.rewardMissing": "GRPO precisa de pelo menos uma função de recompensa. Adicione uma em Mais opções.", + "checks.fineTune.resume": "O treino continua a partir de {{path}}.", + "checks.fineTune.accessNone": "Nenhum token definido. Um modelo com acesso restrito precisa de um token do Hugging Face em Mais opções.", + "checks.fineTune.accessSet": "Um token do Hugging Face está definido.", + "checks.quantize.modelOk": "Quantizando {{model}}.", + "checks.quantize.modelMissing": "Informe o modelo a quantizar.", + "checks.quantize.typeMissing": "Informe o tipo de quantização personalizado.", + "checks.quantize.backendOk": "{{name}} está instalado.", + "checks.quantize.backendMissing": "Nenhum backend de quantização está instalado, então o job pode não iniciar.", + "checks.quantize.backendOther": "{{name}} não está entre os backends de quantização instalados ({{list}}).", + "checks.quantize.backendUnknown": "Não foi possível ler os backends instalados.", + "checks.quantize.memory": "{{free}} de {{total}} de RAM livres. A conversão roda na CPU.", + "checks.quantize.disk": "{{free}} livres no disco de modelos. O download, o arquivo convertido e o resultado são gravados em disco.", + "checks.quantize.diskLow": "Só {{free}} livres no disco de modelos. O download, o arquivo convertido e o resultado são gravados em disco.", + "checks.quantize.accessNone": "Nenhum token definido. Um modelo com acesso restrito precisa de um token do Hugging Face em Mais opções.", + "checks.quantize.accessSet": "Um token do Hugging Face está definido.", + "checks.import.sourceOk_huggingface": "Reconhecido como um repositório do Hugging Face. O LocalAI verifica os arquivos dele ao importar.", + "checks.import.sourceOk_url": "Formato de URL reconhecido. O conteúdo não foi verificado.", + "checks.import.sourceOk_config": "Reconhecido como um arquivo de configuração de modelo. Ele é lido e usado como está.", + "checks.import.sourceOk_oci": "Reconhecido como uma referência de imagem OCI.", + "checks.import.sourceOk_ollama": "Reconhecido como uma referência de modelo do Ollama.", + "checks.import.sourceOk_local": "Reconhecido como um caminho na máquina que executa o LocalAI.", + "checks.import.sourceOk": "A origem foi reconhecida.", + "checks.import.sourceUnknown": "Isto não parece com nenhum formato que o LocalAI conheça. Ele vai tentar mesmo assim, e pode falhar.", + "checks.import.backendDownload": "{{name}} ainda não está instalado. O LocalAI o baixa primeiro.", + "checks.import.backendChosen": "{{name}} foi escolhido e está instalado.", + "checks.import.backendAuto": "O LocalAI escolhe o backend pelos arquivos da origem quando a importação começa.", + "checks.import.nameTaken": "Já existe um modelo instalado chamado {{name}}.", + "checks.import.nameFree": "{{name}} não está em uso.", + "checks.import.nameAuto": "O modelo recebe o nome da origem.", + "checks.import.disk": "{{free}} livres no disco de modelos. O tamanho do download é conhecido quando a importação começa.", + "checks.import.memory_gpu": "{{free}} de memória da GPU estão livres agora. O que este modelo precisa é conhecido quando a importação começa.", + "checks.import.memory_ram": "{{free}} de RAM estão livres agora. O que este modelo precisa é conhecido quando a importação começa.", + "checks.import.memory_cluster": "{{free}} estão disponíveis no melhor nó. O que este modelo precisa é conhecido quando a importação começa.", + "checks.import.memory": "{{free}} de memória estão livres agora. O que este modelo precisa é conhecido quando a importação começa.", + "form.key": "Chave", + "form.value": "Valor", + "form.addOption": "Adicionar opção", + "form.copy": "Copiar para a área de transferência", + "form.more": "Mais opções", + "form.moreHint": "método, backend, adaptador, otimizador, avaliação, funções de recompensa", + "form.baseModel": "Modelo base", + "form.model": "Modelo", + "form.modelHint": "Um id do Hugging Face ou um caminho nesta máquina.", + "form.data": "Seus dados", + "form.dataset": "Origem do dataset", + "form.datasetHint": "Um id de dataset do Hugging Face, ou envie um arquivo.", + "form.split": "Divisão", + "form.upload": "Enviar um arquivo no lugar", + "form.uploadHint": ".json, .jsonl ou .csv", + "form.removeFile": "Remover o arquivo", + "form.train": "O que treinar", + "form.kind": "Tipo de treino", + "form.kinds.adapter.title": "Adaptador", + "form.kinds.adapter.text": "Treina um adaptador pequeno e mantém os pesos base intactos. A escolha usual, e precisa de pouca memória.", + "form.kinds.full.title": "Modelo completo", + "form.kinds.full.text": "Altera todos os pesos. Precisa de várias vezes a memória de um adaptador.", + "form.epochs": "Épocas", + "form.batch": "Tamanho do lote", + "form.lr": "Taxa de aprendizado", + "form.backend": "Backend", + "form.method": "Método de treino", + "form.adapterKind": "Tipo de adaptador", + "form.rank": "Rank", + "form.alpha": "Alfa", + "form.dropout": "Dropout", + "form.gradAccum": "Passos de acumulação de gradiente", + "form.warmup": "Passos de aquecimento", + "form.seqLen": "Comprimento máximo da sequência", + "form.optimizer": "Otimizador", + "form.maxSteps": "Máximo de passos", + "form.maxStepsHint": "0 para automático.", + "form.saveSteps": "Passos para salvar", + "form.saveLimit": "Limite total de salvamentos", + "form.saveLimitHint": "0 mantém todos os checkpoints.", + "form.weightDecay": "Decaimento de peso", + "form.seed": "Semente", + "form.seedHint": "0 escolhe uma semente aleatória.", + "form.precision": "Precisão mista", + "form.auto": "Automático", + "form.targetModules": "Módulos alvo", + "form.targetModulesHint": "Separados por vírgula. Deixe em branco para o padrão do backend.", + "form.gradCheckpointing": "Checkpointing de gradiente (mais lento, usa menos memória)", + "form.token": "Token do Hugging Face", + "form.tokenHint": "Só é necessário para modelos com acesso restrito.", + "form.evalEnable": "Avaliar durante o treino", + "form.evalStrategy": "Estratégia de avaliação", + "form.evalSteps_": "Passos", + "form.evalEpoch": "Época", + "form.evalSteps": "Passos de avaliação", + "form.evalStepsHint": "0 usa os passos para salvar.", + "form.evalSplit": "Divisão de avaliação", + "form.evalDataset": "Dataset de avaliação", + "form.evalDatasetPlaceholder": "Um dataset separado", + "form.evalRatio": "Proporção da divisão automática", + "form.rewards": "Funções de recompensa (GRPO)", + "form.rewardsHint": "GRPO precisa de pelo menos uma função de recompensa. Escolha as integradas ou adicione as suas.", + "form.resume": "Retomar do checkpoint", + "form.clearCheckpoint": "Limpar o checkpoint", + "form.extra": "Opções extras", + "form.extraHint": "Pares de chave e valor específicos do backend.", + "form.summaryFull": "modelo completo", + "form.summary_lora": "adaptador LoRA", + "form.summary_loha": "adaptador LoHa", + "form.summary_lokr": "adaptador LoKr", + "form.recipe": "Treinar um {{kind}} de {{model}} com {{method}}, {{epochs}} épocas sobre {{data}}.", + "form.recipeEmpty": "Escolha um modelo e um dataset para ver o que será treinado.", + "rewards.format_reward": "Verifica o formato ... seguido da resposta.", + "rewards.reasoning_accuracy_reward": "Compara o conteúdo de com a coluna de resposta do dataset.", + "rewards.length_reward": "Pontua o quanto a resposta se aproxima de um comprimento alvo.", + "rewards.xml_tag_reward": "Pontua tags XML abertas e fechadas corretamente.", + "rewards.no_repetition_reward": "Penaliza n-gramas repetidos.", + "rewards.code_execution_reward": "Verifica se os blocos de código Python têm sintaxe válida.", + "rewards.param_target_length": "Comprimento alvo", + "rewards.fnName": "Nome da função", + "rewards.fnBody": "Corpo da função", + "rewards.fnHint": "Recebe completions e kwargs e deve retornar uma lista de floats. re, math, json e string estão disponíveis.", + "rewards.add": "Adicionar", + "rewards.cancel": "Cancelar", + "rewards.custom": "Adicionar uma função de recompensa personalizada", + "fineTune.title": "Ajuste fino", + "fineTune.lede": "Treine um adaptador com os seus próprios dados e depois exporte-o como um modelo com o qual você pode conversar.", + "fineTune.importConfig": "Importar configuração", + "fineTune.exportConfig": "Exportar configuração", + "fineTune.newJob": "Novo job", + "fineTune.badConfig": "Não foi possível ler o arquivo. Use uma configuração JSON exportada desta página.", + "fineTune.start": "Iniciar ajuste fino", + "fineTune.starting": "Iniciando...", + "fineTune.resume": "Retomar treino", + "fineTune.resuming": "Retomando do checkpoint:", + "fineTune.clear": "Limpar", + "fineTune.barNote": "Nada é alterado até você iniciar.", + "fineTune.jobMeta": "{{kind}} {{method}} em {{backend}}, iniciado {{started}}", + "fineTune.failed": "O treino falhou", + "fineTune.failedAt": "O treino parou no passo {{step}} de {{total}}", + "fineTune.noMessage": "O backend não enviou mensagem.", + "fineTune.finished": "Treino concluído.", + "fineTune.finishedLoss": "Treino concluído. A última perda foi {{loss}}.", + "fineTune.stopped": "O job foi interrompido. Um checkpoint é mantido se você escolheu mantê-lo.", + "fineTune.tryTitle": "O que fazer a seguir", + "fineTune.tryText": "O servidor informou que a memória acabou. Cada alteração abaixo é aplicada a uma cópia da configuração, e nada começa até você pressionar Iniciar.", + "fineTune.tryPlain": "Abra a configuração de novo, altere o que a mensagem aponta e inicie um novo job.", + "fineTune.fixBatch": "Reduzir o tamanho do lote para 1", + "fineTune.fixCheckpointing": "Ativar o checkpointing de gradiente (mais lento, usa menos memória)", + "fineTune.retryChanges": "Abrir a configuração com estas alterações", + "fineTune.reuse": "Abrir a configuração de novo", + "fineTune.resumeFrom": "Retomar do passo {{step}}", + "fineTune.status.queued": "Na fila", + "fineTune.status.loading_model": "Carregando modelo", + "fineTune.status.loading_dataset": "Carregando dataset", + "fineTune.status.training": "Treinando", + "fineTune.status.saving": "Salvando", + "fineTune.status.completed": "Concluído", + "fineTune.status.failed": "Falhou", + "fineTune.status.stopped": "Parado", + "stop.title": "Parar o treino?", + "stop.text": "Um checkpoint salvo agora permite retomar ou exportar o que foi aprendido até aqui.", + "stop.keep": "Parar e manter um checkpoint", + "stop.discard": "Parar sem checkpoint", + "stop.cancel": "Continuar treinando", + "stop.cancelDelete": "Cancelar", + "chart.loss": "Perda", + "chart.learning_rate": "Taxa de aprendizado", + "chart.grad_norm": "Norma do gradiente", + "chart.metric": "Métrica", + "chart.step": "passo", + "chart.eval": "Perda de avaliação", + "chart.waiting": "Aguardando mais passos.", + "chart.window": "Passos de {{from}} a {{to}} de {{total}}", + "chart.windowEval": "Passos de {{from}} a {{to}} de {{total}}. Os pontos vazios são a perda de avaliação.", + "chart.aria": "{{metric}} ao longo dos passos de treino. O último valor é {{last}} no passo {{step}}.", + "chart.readout": "passo {{step}}: {{value}}", + "chart.dataTitle": "Mostrar os números", + "chart.emptyText": "O gráfico é desenhado quando o job informar dois passos.", + "log.title": "Log", + "log.note": "O que o job informou desde que esta página foi aberta.", + "log.only": "Somente avisos e erros", + "log.copy": "Copiar", + "log.copied": "Copiado", + "log.download": "Baixar", + "log.empty": "Nada informado ainda.", + "log.noneFiltered": "Nenhum aviso ou erro.", + "checkpoints.title": "Checkpoints", + "checkpoints.step": "Passo", + "checkpoints.epoch": "Época", + "checkpoints.loss": "Perda", + "checkpoints.path": "Caminho", + "checkpoints.actions": "Ações", + "checkpoints.resume": "Retomar", + "checkpoints.resumeTitle": "Retomar o treino a partir deste checkpoint", + "checkpoints.export": "Exportar", + "checkpoints.exportTitle": "Exportar este checkpoint", + "export.blocked": "A exportação está desativada neste servidor porque ele não compartilha a pasta de modelos com o host do complemento.", + "export.title": "Exportar como modelo", + "export.lede": "Exportar transforma o resultado em um modelo que o LocalAI pode carregar.", + "export.checkpoint": "Checkpoint", + "export.final": "Modelo final (diretório de saída)", + "export.checkpointOption": "Passo {{step}} (perda {{loss}})", + "export.format": "Formato de exportação", + "export.lora": "Adaptador LoRA", + "export.merged16": "Mesclado (16 bits)", + "export.merged4": "Mesclado (4 bits)", + "export.quant": "Quantização", + "export.name": "Nome do modelo", + "export.nameHint": "Deixe em branco para gerar um nome a partir do job.", + "export.go": "Exportar", + "export.exporting": "Exportando...", + "export.working": "Exportação em andamento...", + "export.done": "Modelo exportado e registrado como \"{{name}}\"", + "export.failed": "A exportação falhou: {{message}}", + "export.unknown": "erro desconhecido", + "export.nextTitle": "{{name}} agora é um modelo", + "export.nextText": "Ele está registrado no LocalAI. Abra um chat com ele ou encontre-o em Modelos.", + "export.chat": "Conversar com {{name}}", + "export.models": "Abrir em Modelos", + "export.archive": "Baixar arquivo", + "jobs.title": "Jobs anteriores", + "jobs.count_one": "{{count}} mantido neste servidor", + "jobs.count_other": "{{count}} mantidos neste servidor", + "jobs.loading": "Carregando jobs", + "jobs.emptyTitle": "Nenhum job de ajuste fino ainda", + "jobs.emptyText": "Configure um acima. Ele aparece aqui com o seu status, e você pode reutilizar a configuração depois.", + "jobs.model": "Modelo", + "jobs.kind": "Tipo", + "jobs.status": "Status", + "jobs.started": "Iniciado", + "jobs.actions": "Ações", + "jobs.open": "Abrir este job", + "jobs.reuse": "Reutilizar", + "jobs.reuseTitle": "Usar a configuração deste job em um novo job", + "jobs.delete": "Excluir", + "jobs.deleteTitle": "Excluir este job e os seus dados", + "jobs.deleteDialogTitle": "Excluir este job?", + "jobs.deleteDialogText": "Isto exclui o job com os seus checkpoints e qualquer modelo exportado. Não pode ser desfeito.", + "off.badge": "Não disponível para você", + "off.fineTune.title": "Sua conta não pode fazer ajuste fino", + "off.fineTune.text": "O ajuste fino é uma permissão separada, e esta conta não a tem.", + "off.quantize.title": "Sua conta não pode quantizar", + "off.quantize.text": "A quantização é uma permissão separada, e esta conta não a tem.", + "off.admin": "Um administrador pode ativá-la em Usuários e chaves.", + "off.askAdmin": "Peça a um administrador para ativá-la na sua conta.", + "off.back": "Ir para o Início", + "landing.experimental": "Experimental", + "run.unknownPercent": "Progresso ainda não informado", + "export.adminOnly.title": "Exportar um modelo é função dos administradores", + "export.adminOnly.text": "Exportar adiciona um modelo a este servidor para todos que podem usá-lo. Peça a um administrador para exportar este trabalho." +} diff --git a/addons/fine-tuning/ui/src/i18n/fine-tuning.zh-CN.json b/addons/fine-tuning/ui/src/i18n/fine-tuning.zh-CN.json new file mode 100644 index 0000000000..69f7e4e446 --- /dev/null +++ b/addons/fine-tuning/ui/src/i18n/fine-tuning.zh-CN.json @@ -0,0 +1,295 @@ +{ + "steps.label": "进度", + "steps.setup": "设置", + "steps.check": "检查", + "steps.run": "运行", + "steps.result": "结果", + "run.stop": "停止", + "run.connecting": "正在连接任务。等待第一个进度事件。", + "run.progress": "进度", + "run.stages": "阶段", + "run.step": "步", + "run.of": "/", + "run.epoch": "轮次", + "run.tokens": "tokens/s", + "run.eta": "约剩 {{time}}", + "run.etaSeconds": "约剩 {{seconds}} 秒", + "checks.heading": "开始前检查", + "checks.live": "随您修改设置而更新。", + "checks.meterLabel": "已用 {{used}}%,{{total}} 中 {{free}} 可用", + "checks.openBackends": "打开后端", + "checks.barOk": "没有任何项目阻止启动。", + "checks.barWarn_one": "{{count}} 条警告。仍可以启动。", + "checks.barWarn_other": "{{count}} 条警告。仍可以启动。", + "checks.title.model": "模型", + "checks.title.data": "数据集", + "checks.title.backend": "后端", + "checks.title.memory": "内存", + "checks.title.disk": "磁盘", + "checks.title.reward": "奖励函数", + "checks.title.resume": "恢复", + "checks.title.access": "模型访问", + "checks.title.type": "量化类型", + "checks.title.source": "来源", + "checks.title.name": "名称", + "checks.fineTune.modelOk": "正在训练 {{model}}。", + "checks.fineTune.modelMissing": "请输入要训练的模型。", + "checks.fineTune.dataOk": "数据来自 {{source}}。", + "checks.fineTune.dataMissing": "请添加数据集:Hugging Face id 或上传的文件。", + "checks.fineTune.backendOk": "{{name}} 已安装。", + "checks.fineTune.backendMissing": "未安装微调后端,任务可能无法启动。", + "checks.fineTune.backendOther": "{{name}} 不在已安装的微调后端({{list}})中。", + "checks.fineTune.backendUnknown": "无法读取已安装的后端。", + "checks.fineTune.memoryGpu": "{{name}}:{{total}} 中 {{free}} 可用。批次大小 {{batch}},序列长度 {{seq}},梯度累积 {{accum}}。任务所需的内存取决于模型和这些设置,LocalAI 在任务开始前不做估算。", + "checks.fineTune.memoryCluster": "最佳节点({{node}})上的 {{total}}。批次大小 {{batch}},序列长度 {{seq}}。任务所需的内存取决于模型和这些设置。", + "checks.fineTune.memoryCpu": "未找到 GPU。训练使用 CPU,RAM {{total}} 中有 {{free}} 可用。预计会很慢。批次大小 {{batch}},序列长度 {{seq}}。", + "checks.fineTune.memoryUnknown": "此账户无法读取机器的内存。批次大小 {{batch}},序列长度 {{seq}}。任务所需的内存取决于模型和这些设置。", + "checks.fineTune.disk": "模型磁盘上可用 {{free}}。下载和检查点需要空间。", + "checks.fineTune.diskLow": "模型磁盘上仅剩 {{free}} 可用。下载和检查点需要空间。", + "checks.fineTune.rewardMissing": "GRPO 至少需要一个奖励函数。请在“更多选项”中添加。", + "checks.fineTune.resume": "训练将从 {{path}} 继续。", + "checks.fineTune.accessNone": "未设置令牌。受限模型需要在“更多选项”中设置 Hugging Face 令牌。", + "checks.fineTune.accessSet": "已设置 Hugging Face 令牌。", + "checks.quantize.modelOk": "正在量化 {{model}}。", + "checks.quantize.modelMissing": "请输入要量化的模型。", + "checks.quantize.typeMissing": "请输入自定义量化类型。", + "checks.quantize.backendOk": "{{name}} 已安装。", + "checks.quantize.backendMissing": "未安装量化后端,任务可能无法启动。", + "checks.quantize.backendOther": "{{name}} 不在已安装的量化后端({{list}})中。", + "checks.quantize.backendUnknown": "无法读取已安装的后端。", + "checks.quantize.memory": "RAM {{total}} 中有 {{free}} 可用。转换在 CPU 上运行。", + "checks.quantize.disk": "模型磁盘上可用 {{free}}。下载、转换后的文件和结果都会写入磁盘。", + "checks.quantize.diskLow": "模型磁盘上仅剩 {{free}} 可用。下载、转换后的文件和结果都会写入磁盘。", + "checks.quantize.accessNone": "未设置令牌。受限模型需要在“更多选项”中设置 Hugging Face 令牌。", + "checks.quantize.accessSet": "已设置 Hugging Face 令牌。", + "checks.import.sourceOk_huggingface": "识别为 Hugging Face 仓库。导入时 LocalAI 会检查其中的文件。", + "checks.import.sourceOk_url": "已识别 URL 格式。其内容尚未检查。", + "checks.import.sourceOk_config": "识别为模型配置文件。将按原样读取并使用。", + "checks.import.sourceOk_oci": "识别为 OCI 镜像引用。", + "checks.import.sourceOk_ollama": "识别为 Ollama 模型引用。", + "checks.import.sourceOk_local": "识别为运行 LocalAI 的机器上的路径。", + "checks.import.sourceOk": "已识别来源。", + "checks.import.sourceUnknown": "这看起来不是 LocalAI 支持的任何格式。仍会尝试,但可能失败。", + "checks.import.backendDownload": "{{name}} 尚未安装。LocalAI 会先下载它。", + "checks.import.backendChosen": "已选择并安装 {{name}}。", + "checks.import.backendAuto": "导入开始时,LocalAI 会根据来源的文件选择后端。", + "checks.import.nameTaken": "名为 {{name}} 的模型已安装。", + "checks.import.nameFree": "{{name}} 未被使用。", + "checks.import.nameAuto": "模型将以来源命名。", + "checks.import.disk": "模型磁盘上可用 {{free}}。导入开始后即可知道下载大小。", + "checks.import.memory_gpu": "当前 GPU 显存可用 {{free}}。此模型需要多少,导入开始后即可知道。", + "checks.import.memory_ram": "当前 RAM 可用 {{free}}。此模型需要多少,导入开始后即可知道。", + "checks.import.memory_cluster": "最佳节点上可用 {{free}}。此模型需要多少,导入开始后即可知道。", + "checks.import.memory": "当前内存可用 {{free}}。此模型需要多少,导入开始后即可知道。", + "form.key": "键", + "form.value": "值", + "form.addOption": "添加选项", + "form.copy": "复制到剪贴板", + "form.more": "更多选项", + "form.moreHint": "方法、后端、适配器、优化器、评估、奖励函数", + "form.baseModel": "基础模型", + "form.model": "模型", + "form.modelHint": "Hugging Face id,或本机上的路径。", + "form.data": "您的数据", + "form.dataset": "数据集来源", + "form.datasetHint": "Hugging Face 数据集 id,或上传文件。", + "form.split": "划分", + "form.upload": "改为上传文件", + "form.uploadHint": ".json、.jsonl 或 .csv", + "form.removeFile": "移除文件", + "form.train": "训练内容", + "form.kind": "训练类型", + "form.kinds.adapter.title": "适配器", + "form.kinds.adapter.text": "训练一个小型适配器,保持基础权重不变。这是常见的选择,所需内存很少。", + "form.kinds.full.title": "完整模型", + "form.kinds.full.text": "修改每一个权重。所需内存是适配器的数倍。", + "form.epochs": "轮次", + "form.batch": "批次大小", + "form.lr": "学习率", + "form.backend": "后端", + "form.method": "训练方法", + "form.adapterKind": "适配器类型", + "form.rank": "秩", + "form.alpha": "Alpha", + "form.dropout": "Dropout", + "form.gradAccum": "梯度累积步数", + "form.warmup": "预热步数", + "form.seqLen": "最大序列长度", + "form.optimizer": "优化器", + "form.maxSteps": "最大步数", + "form.maxStepsHint": "0 表示自动。", + "form.saveSteps": "保存步数", + "form.saveLimit": "保存总数上限", + "form.saveLimitHint": "0 表示保留所有检查点。", + "form.weightDecay": "权重衰减", + "form.seed": "随机种子", + "form.seedHint": "0 表示随机选择种子。", + "form.precision": "混合精度", + "form.auto": "自动", + "form.targetModules": "目标模块", + "form.targetModulesHint": "以逗号分隔。留空则使用后端默认值。", + "form.gradCheckpointing": "梯度检查点(较慢,但占用更少内存)", + "form.token": "Hugging Face 令牌", + "form.tokenHint": "仅受限模型需要。", + "form.evalEnable": "训练期间评估", + "form.evalStrategy": "评估策略", + "form.evalSteps_": "步", + "form.evalEpoch": "轮次", + "form.evalSteps": "评估步数", + "form.evalStepsHint": "0 表示与保存步数相同。", + "form.evalSplit": "评估划分", + "form.evalDataset": "评估数据集", + "form.evalDatasetPlaceholder": "单独的数据集", + "form.evalRatio": "自动划分比例", + "form.rewards": "奖励函数(GRPO)", + "form.rewardsHint": "GRPO 至少需要一个奖励函数。可选择内置函数,或添加您自己的。", + "form.resume": "从检查点恢复", + "form.clearCheckpoint": "清除检查点", + "form.extra": "额外选项", + "form.extraHint": "特定于后端的键值对。", + "form.summaryFull": "完整模型", + "form.summary_lora": "LoRA 适配器", + "form.summary_loha": "LoHa 适配器", + "form.summary_lokr": "LoKr 适配器", + "form.recipe": "使用 {{method}} 在 {{data}} 上训练 {{model}} 的 {{kind}},共 {{epochs}} 轮。", + "form.recipeEmpty": "选择模型和数据集,即可查看将要训练的内容。", + "rewards.format_reward": "检查 ... 再接答案的格式。", + "rewards.reasoning_accuracy_reward": "将 的内容与数据集的答案列进行比较。", + "rewards.length_reward": "评估答案与目标长度的接近程度。", + "rewards.xml_tag_reward": "评估 XML 标签是否正确地成对打开和关闭。", + "rewards.no_repetition_reward": "对重复的 n-gram 进行扣分。", + "rewards.code_execution_reward": "检查 Python 代码块的语法是否有效。", + "rewards.param_target_length": "目标长度", + "rewards.fnName": "函数名", + "rewards.fnBody": "函数体", + "rewards.fnHint": "接收 completions 和 kwargs,并必须返回浮点数列表。可以使用 re、math、json 和 string。", + "rewards.add": "添加", + "rewards.cancel": "取消", + "rewards.custom": "添加自定义奖励函数", + "fineTune.title": "微调", + "fineTune.lede": "用您自己的数据训练适配器,然后将其导出为可以对话的模型。", + "fineTune.importConfig": "导入配置", + "fineTune.exportConfig": "导出配置", + "fineTune.newJob": "新建任务", + "fineTune.badConfig": "无法读取该文件。请使用从此页面导出的 JSON 配置。", + "fineTune.start": "开始微调", + "fineTune.starting": "正在启动...", + "fineTune.resume": "恢复训练", + "fineTune.resuming": "正在从检查点恢复:", + "fineTune.clear": "清除", + "fineTune.barNote": "在您开始之前,不会更改任何内容。", + "fineTune.jobMeta": "{{backend}} 上的 {{kind}} {{method}},开始于 {{started}}", + "fineTune.failed": "训练失败", + "fineTune.failedAt": "训练在第 {{step}} 步(共 {{total}} 步)时停止", + "fineTune.noMessage": "后端没有提供消息。", + "fineTune.finished": "训练完成。", + "fineTune.finishedLoss": "训练完成。最后的损失为 {{loss}}。", + "fineTune.stopped": "任务已停止。如果您选择保留,检查点会被保留。", + "fineTune.tryTitle": "下一步该怎么做", + "fineTune.tryText": "服务器报告内存不足。下面的每项更改都会应用到设置的副本上,在您点击“开始”之前不会启动任何内容。", + "fineTune.tryPlain": "重新打开设置,修改消息所指的内容,然后启动新任务。", + "fineTune.fixBatch": "将批次大小降至 1", + "fineTune.fixCheckpointing": "开启梯度检查点(较慢,但占用更少内存)", + "fineTune.retryChanges": "应用这些更改并打开设置", + "fineTune.reuse": "重新打开设置", + "fineTune.resumeFrom": "从第 {{step}} 步恢复", + "fineTune.status.queued": "排队中", + "fineTune.status.loading_model": "正在加载模型", + "fineTune.status.loading_dataset": "正在加载数据集", + "fineTune.status.training": "训练中", + "fineTune.status.saving": "保存中", + "fineTune.status.completed": "已完成", + "fineTune.status.failed": "失败", + "fineTune.status.stopped": "已停止", + "stop.title": "停止训练?", + "stop.text": "立即保存的检查点可让您恢复训练,或导出目前已学到的内容。", + "stop.keep": "停止并保留检查点", + "stop.discard": "停止且不保留检查点", + "stop.cancel": "继续训练", + "stop.cancelDelete": "取消", + "chart.loss": "损失", + "chart.learning_rate": "学习率", + "chart.grad_norm": "梯度范数", + "chart.metric": "指标", + "chart.step": "步", + "chart.eval": "评估损失", + "chart.waiting": "等待更多步数。", + "chart.window": "第 {{from}} 至 {{to}} 步,共 {{total}} 步", + "chart.windowEval": "第 {{from}} 至 {{to}} 步,共 {{total}} 步。空心点为评估损失。", + "chart.aria": "训练步数上的 {{metric}}。第 {{step}} 步的最后一个值为 {{last}}。", + "chart.readout": "第 {{step}} 步:{{value}}", + "chart.dataTitle": "显示数值", + "chart.emptyText": "任务报告两个步骤后,图表即会绘制。", + "log.title": "日志", + "log.note": "自打开此页面以来任务报告的内容。", + "log.only": "仅显示警告和错误", + "log.copy": "复制", + "log.copied": "已复制", + "log.download": "下载", + "log.empty": "尚无报告。", + "log.noneFiltered": "没有警告或错误。", + "checkpoints.title": "检查点", + "checkpoints.step": "步", + "checkpoints.epoch": "轮次", + "checkpoints.loss": "损失", + "checkpoints.path": "路径", + "checkpoints.actions": "操作", + "checkpoints.resume": "恢复", + "checkpoints.resumeTitle": "从此检查点恢复训练", + "checkpoints.export": "导出", + "checkpoints.exportTitle": "导出此检查点", + "export.blocked": "此服务器未与附加组件主机共享模型文件夹,因此已关闭导出。", + "export.title": "导出为模型", + "export.lede": "导出会将结果转换为 LocalAI 可以加载的模型。", + "export.checkpoint": "检查点", + "export.final": "最终模型(输出目录)", + "export.checkpointOption": "第 {{step}} 步(损失 {{loss}})", + "export.format": "导出格式", + "export.lora": "LoRA 适配器", + "export.merged16": "合并(16 位)", + "export.merged4": "合并(4 位)", + "export.quant": "量化", + "export.name": "模型名称", + "export.nameHint": "留空则根据任务自动生成。", + "export.go": "导出", + "export.exporting": "正在导出...", + "export.working": "正在导出...", + "export.done": "模型已导出并注册为“{{name}}”", + "export.failed": "导出失败:{{message}}", + "export.unknown": "未知错误", + "export.nextTitle": "{{name}} 现已成为模型", + "export.nextText": "它已在 LocalAI 中注册。打开与它的对话,或在模型页面中找到它。", + "export.chat": "与 {{name}} 对话", + "export.models": "在模型中打开", + "export.archive": "下载归档", + "jobs.title": "早前的任务", + "jobs.count_one": "此服务器保留 {{count}} 个", + "jobs.count_other": "此服务器保留 {{count}} 个", + "jobs.loading": "正在加载任务", + "jobs.emptyTitle": "还没有微调任务", + "jobs.emptyText": "在上方进行设置。任务会连同状态显示在这里,之后您可以复用其设置。", + "jobs.model": "模型", + "jobs.kind": "类型", + "jobs.status": "状态", + "jobs.started": "开始时间", + "jobs.actions": "操作", + "jobs.open": "打开此任务", + "jobs.reuse": "复用", + "jobs.reuseTitle": "将此任务的设置用于新任务", + "jobs.delete": "删除", + "jobs.deleteTitle": "删除此任务及其数据", + "jobs.deleteDialogTitle": "删除此任务?", + "jobs.deleteDialogText": "这将删除任务及其检查点和任何已导出的模型。此操作无法撤销。", + "off.badge": "您无法使用", + "off.fineTune.title": "您的账户无法进行微调", + "off.fineTune.text": "微调是一项单独的权限,此账户没有该权限。", + "off.quantize.title": "您的账户无法进行量化", + "off.quantize.text": "量化是一项单独的权限,此账户没有该权限。", + "off.admin": "管理员可以在“用户和密钥”中开启该权限。", + "off.askAdmin": "请管理员为您的账户开启该权限。", + "off.back": "前往首页", + "landing.experimental": "实验性", + "run.unknownPercent": "尚未报告进度", + "export.adminOnly.title": "导出模型仅限管理员", + "export.adminOnly.text": "导出会为所有可使用此服务器的人添加一个模型。请让管理员导出此任务。" +} diff --git a/addons/fine-tuning/ui/src/i18n/index.js b/addons/fine-tuning/ui/src/i18n/index.js new file mode 100644 index 0000000000..ed7c6df1f9 --- /dev/null +++ b/addons/fine-tuning/ui/src/i18n/index.js @@ -0,0 +1,16 @@ +// The words of the fine-tuning pages, one JSON bundle per language of the web UI. +// English is the source and the fallback of every key. The texts repeat the +// host's own tools.json for each language; the few keys the add-on owns +// are drafts that want a native review. addons/internal/i18ncheck +// checks that every bundle has the keys and placeholders of English. +import en from './fine-tuning.en.json' +import it from './fine-tuning.it.json' +import de from './fine-tuning.de.json' +import es from './fine-tuning.es.json' +import fr from './fine-tuning.fr.json' +import id from './fine-tuning.id.json' +import ko from './fine-tuning.ko.json' +import pt_BR from './fine-tuning.pt-BR.json' +import zh_CN from './fine-tuning.zh-CN.json' + +export const locales = { en, it, de, es, fr, id, ko, 'pt-BR': pt_BR, 'zh-CN': zh_CN } diff --git a/addons/fine-tuning/ui/test/page.test.mjs b/addons/fine-tuning/ui/test/page.test.mjs index b376d754f2..418463c123 100644 --- a/addons/fine-tuning/ui/test/page.test.mjs +++ b/addons/fine-tuning/ui/test/page.test.mjs @@ -4,9 +4,9 @@ import { readFileSync, readdirSync } from 'node:fs' import { join } from 'node:path' import { load, draw, here, hostUi, React, renderToString } from './load.mjs' import { createApi } from '../src/api.js' -import en from '../src/i18n/en.js' const read = (p) => readFileSync(p, 'utf8') +const en = JSON.parse(read(join(here, '../src/i18n/fine-tuning.en.json'))) const src = join(here, '../src') const toolsUi = join(here, '../../../internal/tools-ui/src') const files = (dir) => readdirSync(dir, { recursive: true }).filter(f => /\.(jsx?|mjs)$/.test(f)).map(f => join(dir, f)) @@ -208,7 +208,7 @@ test('plural: the job count picks the singular and the plural key', async () => assert.equal(ctx.plural('jobs.count', 3), '3 kept on this server') }) -test('i18n: every literal key the add-on and the shared views read is in en.js', () => { +test('i18n: every literal key the add-on and the shared views read is in the English bundle', () => { const keys = new Set() for (const text of [ownText, sharedText]) for (const m of text.matchAll(/\bt\(\s*'([A-Za-z0-9_.]+)'/g)) keys.add(m[1]) assert.ok(keys.size > 100, `found only ${keys.size} literal keys`) @@ -216,7 +216,7 @@ test('i18n: every literal key the add-on and the shared views read is in en.js', assert.deepEqual(missing, []) }) -test('i18n: en.js repeats the host strings and adds only the keys the add-on owns', () => { +test('i18n: the English bundle repeats the host strings and adds only the keys the add-on owns', () => { const tools = Object.fromEntries(flatten(JSON.parse(read(join(hostUi, 'public/locales/en/tools.json'))))) const own = [] for (const [k, v] of Object.entries(en)) { diff --git a/addons/memory/ui/src/i18n/index.js b/addons/memory/ui/src/i18n/index.js new file mode 100644 index 0000000000..2444861064 --- /dev/null +++ b/addons/memory/ui/src/i18n/index.js @@ -0,0 +1,16 @@ +// The words of the memory page, one JSON bundle per language of the web UI. +// English is the source and the fallback of every key. Italian is a draft that +// wants a native review; the other bundles repeat English except for the few +// strings the host already translated. addons/internal/i18ncheck checks that +// every bundle has the keys and placeholders of English. +import en from './memory.en.json' +import it from './memory.it.json' +import de from './memory.de.json' +import es from './memory.es.json' +import fr from './memory.fr.json' +import id from './memory.id.json' +import ko from './memory.ko.json' +import pt_BR from './memory.pt-BR.json' +import zh_CN from './memory.zh-CN.json' + +export const locales = { en, it, de, es, fr, id, ko, 'pt-BR': pt_BR, 'zh-CN': zh_CN } diff --git a/addons/memory/ui/src/i18n/memory.de.json b/addons/memory/ui/src/i18n/memory.de.json new file mode 100644 index 0000000000..c01b85e0f5 --- /dev/null +++ b/addons/memory/ui/src/i18n/memory.de.json @@ -0,0 +1,26 @@ +{ + "memory.title": "Short memory", + "memory.intro": "Your chats keep short notes about you: where you live, what you like, how you want answers. Before a reply, the model reads the note that matches the question. You do not have to ask it to.", + "memory.rules.confirm": "By default a new note is saved only after you confirm it. A proposal waits for one day, then it expires. You can change this below.", + "memory.rules.where": "The notes are plain files in the Memory folder of your files. You can read, edit and delete them there, or on the Memory tab of your account.", + "memory.rules.scopes": "There are three kinds of notes: yours for every chat, one for each saved agent, and one for each conversation that comes from a messaging app. A saved agent reads notes only when you allow this add-on for it.", + "memory.rules.limits": "The model reads a short list, at most 40 lines for each kind, and opens a topic when it needs the details.", + "memory.open": "Open my notes", + "memory.builtin.title": "Memory is built in", + "memory.builtin.reason": "Memory is part of LocalAI now, and you do not need this add-on for it. Choose how notes are saved in Settings > Memory.", + "memory.builtin.fix": "Open Settings > Memory", + "memory.save.title": "Eine Notiz speichern", + "memory.save.legend": "When the assistant wants to save something to my memory", + "memory.save.ask": "Erst fragen", + "memory.save.ask.desc": "The assistant proposes the note and waits. You confirm it in the chat, or answer yes or no in a messaging app. This is the default.", + "memory.save.auto": "Speichern und Bescheid geben", + "memory.save.auto.desc": "In your own chats the note is saved at once, and the chat shows \"Saved to memory\" with an Undo. A reply saves at most 5 notes. A long note, or one that looks like it holds a secret, still waits for you.", + "memory.save.never": "Nie", + "memory.save.never.desc": "The assistant cannot propose a note. It says that saving is off if you ask it to remember something.", + "memory.save.exception": "Chats that come from a messaging app always ask first, whatever you choose here, because the person who wrote the message is not always you. Scheduled and background runs cannot save notes. A saved agent cannot widen your choice: if you choose to be asked, it asks, even when you let it write to memory.", + "memory.save.loading": "Reading your choice", + "memory.save.saving": "Saving your choice", + "memory.save.saved": "Your choice is saved.", + "memory.save.failed": "Your choice could not be saved. The earlier choice is still in force. Try again.", + "memory.save.unreadable": "Your choice could not be read. Reload the page to try again." +} diff --git a/addons/memory/ui/src/i18n/memory.en.json b/addons/memory/ui/src/i18n/memory.en.json new file mode 100644 index 0000000000..f49f5298d8 --- /dev/null +++ b/addons/memory/ui/src/i18n/memory.en.json @@ -0,0 +1,26 @@ +{ + "memory.title": "Short memory", + "memory.intro": "Your chats keep short notes about you: where you live, what you like, how you want answers. Before a reply, the model reads the note that matches the question. You do not have to ask it to.", + "memory.rules.confirm": "By default a new note is saved only after you confirm it. A proposal waits for one day, then it expires. You can change this below.", + "memory.rules.where": "The notes are plain files in the Memory folder of your files. You can read, edit and delete them there, or on the Memory tab of your account.", + "memory.rules.scopes": "There are three kinds of notes: yours for every chat, one for each saved agent, and one for each conversation that comes from a messaging app. A saved agent reads notes only when you allow this add-on for it.", + "memory.rules.limits": "The model reads a short list, at most 40 lines for each kind, and opens a topic when it needs the details.", + "memory.open": "Open my notes", + "memory.builtin.title": "Memory is built in", + "memory.builtin.reason": "Memory is part of LocalAI now, and you do not need this add-on for it. Choose how notes are saved in Settings > Memory.", + "memory.builtin.fix": "Open Settings > Memory", + "memory.save.title": "Saving a note", + "memory.save.legend": "When the assistant wants to save something to my memory", + "memory.save.ask": "Ask me first", + "memory.save.ask.desc": "The assistant proposes the note and waits. You confirm it in the chat, or answer yes or no in a messaging app. This is the default.", + "memory.save.auto": "Save it and tell me", + "memory.save.auto.desc": "In your own chats the note is saved at once, and the chat shows \"Saved to memory\" with an Undo. A reply saves at most 5 notes. A long note, or one that looks like it holds a secret, still waits for you.", + "memory.save.never": "Never", + "memory.save.never.desc": "The assistant cannot propose a note. It says that saving is off if you ask it to remember something.", + "memory.save.exception": "Chats that come from a messaging app always ask first, whatever you choose here, because the person who wrote the message is not always you. Scheduled and background runs cannot save notes. A saved agent cannot widen your choice: if you choose to be asked, it asks, even when you let it write to memory.", + "memory.save.loading": "Reading your choice", + "memory.save.saving": "Saving your choice", + "memory.save.saved": "Your choice is saved.", + "memory.save.failed": "Your choice could not be saved. The earlier choice is still in force. Try again.", + "memory.save.unreadable": "Your choice could not be read. Reload the page to try again." +} diff --git a/addons/memory/ui/src/i18n/memory.es.json b/addons/memory/ui/src/i18n/memory.es.json new file mode 100644 index 0000000000..8de68dea61 --- /dev/null +++ b/addons/memory/ui/src/i18n/memory.es.json @@ -0,0 +1,26 @@ +{ + "memory.title": "Short memory", + "memory.intro": "Your chats keep short notes about you: where you live, what you like, how you want answers. Before a reply, the model reads the note that matches the question. You do not have to ask it to.", + "memory.rules.confirm": "By default a new note is saved only after you confirm it. A proposal waits for one day, then it expires. You can change this below.", + "memory.rules.where": "The notes are plain files in the Memory folder of your files. You can read, edit and delete them there, or on the Memory tab of your account.", + "memory.rules.scopes": "There are three kinds of notes: yours for every chat, one for each saved agent, and one for each conversation that comes from a messaging app. A saved agent reads notes only when you allow this add-on for it.", + "memory.rules.limits": "The model reads a short list, at most 40 lines for each kind, and opens a topic when it needs the details.", + "memory.open": "Open my notes", + "memory.builtin.title": "Memory is built in", + "memory.builtin.reason": "Memory is part of LocalAI now, and you do not need this add-on for it. Choose how notes are saved in Settings > Memory.", + "memory.builtin.fix": "Open Settings > Memory", + "memory.save.title": "Guardar una nota", + "memory.save.legend": "When the assistant wants to save something to my memory", + "memory.save.ask": "Preguntarme antes", + "memory.save.ask.desc": "The assistant proposes the note and waits. You confirm it in the chat, or answer yes or no in a messaging app. This is the default.", + "memory.save.auto": "Guardar y avisarme", + "memory.save.auto.desc": "In your own chats the note is saved at once, and the chat shows \"Saved to memory\" with an Undo. A reply saves at most 5 notes. A long note, or one that looks like it holds a secret, still waits for you.", + "memory.save.never": "Nunca", + "memory.save.never.desc": "The assistant cannot propose a note. It says that saving is off if you ask it to remember something.", + "memory.save.exception": "Chats that come from a messaging app always ask first, whatever you choose here, because the person who wrote the message is not always you. Scheduled and background runs cannot save notes. A saved agent cannot widen your choice: if you choose to be asked, it asks, even when you let it write to memory.", + "memory.save.loading": "Reading your choice", + "memory.save.saving": "Saving your choice", + "memory.save.saved": "Your choice is saved.", + "memory.save.failed": "Your choice could not be saved. The earlier choice is still in force. Try again.", + "memory.save.unreadable": "Your choice could not be read. Reload the page to try again." +} diff --git a/addons/memory/ui/src/i18n/memory.fr.json b/addons/memory/ui/src/i18n/memory.fr.json new file mode 100644 index 0000000000..6a0b0b9999 --- /dev/null +++ b/addons/memory/ui/src/i18n/memory.fr.json @@ -0,0 +1,26 @@ +{ + "memory.title": "Short memory", + "memory.intro": "Your chats keep short notes about you: where you live, what you like, how you want answers. Before a reply, the model reads the note that matches the question. You do not have to ask it to.", + "memory.rules.confirm": "By default a new note is saved only after you confirm it. A proposal waits for one day, then it expires. You can change this below.", + "memory.rules.where": "The notes are plain files in the Memory folder of your files. You can read, edit and delete them there, or on the Memory tab of your account.", + "memory.rules.scopes": "There are three kinds of notes: yours for every chat, one for each saved agent, and one for each conversation that comes from a messaging app. A saved agent reads notes only when you allow this add-on for it.", + "memory.rules.limits": "The model reads a short list, at most 40 lines for each kind, and opens a topic when it needs the details.", + "memory.open": "Open my notes", + "memory.builtin.title": "Memory is built in", + "memory.builtin.reason": "Memory is part of LocalAI now, and you do not need this add-on for it. Choose how notes are saved in Settings > Memory.", + "memory.builtin.fix": "Open Settings > Memory", + "memory.save.title": "Enregistrer une note", + "memory.save.legend": "When the assistant wants to save something to my memory", + "memory.save.ask": "Me demander d'abord", + "memory.save.ask.desc": "The assistant proposes the note and waits. You confirm it in the chat, or answer yes or no in a messaging app. This is the default.", + "memory.save.auto": "Enregistrer et me prévenir", + "memory.save.auto.desc": "In your own chats the note is saved at once, and the chat shows \"Saved to memory\" with an Undo. A reply saves at most 5 notes. A long note, or one that looks like it holds a secret, still waits for you.", + "memory.save.never": "Jamais", + "memory.save.never.desc": "The assistant cannot propose a note. It says that saving is off if you ask it to remember something.", + "memory.save.exception": "Chats that come from a messaging app always ask first, whatever you choose here, because the person who wrote the message is not always you. Scheduled and background runs cannot save notes. A saved agent cannot widen your choice: if you choose to be asked, it asks, even when you let it write to memory.", + "memory.save.loading": "Reading your choice", + "memory.save.saving": "Saving your choice", + "memory.save.saved": "Your choice is saved.", + "memory.save.failed": "Your choice could not be saved. The earlier choice is still in force. Try again.", + "memory.save.unreadable": "Your choice could not be read. Reload the page to try again." +} diff --git a/addons/memory/ui/src/i18n/memory.id.json b/addons/memory/ui/src/i18n/memory.id.json new file mode 100644 index 0000000000..2e7c5a45c1 --- /dev/null +++ b/addons/memory/ui/src/i18n/memory.id.json @@ -0,0 +1,26 @@ +{ + "memory.title": "Short memory", + "memory.intro": "Your chats keep short notes about you: where you live, what you like, how you want answers. Before a reply, the model reads the note that matches the question. You do not have to ask it to.", + "memory.rules.confirm": "By default a new note is saved only after you confirm it. A proposal waits for one day, then it expires. You can change this below.", + "memory.rules.where": "The notes are plain files in the Memory folder of your files. You can read, edit and delete them there, or on the Memory tab of your account.", + "memory.rules.scopes": "There are three kinds of notes: yours for every chat, one for each saved agent, and one for each conversation that comes from a messaging app. A saved agent reads notes only when you allow this add-on for it.", + "memory.rules.limits": "The model reads a short list, at most 40 lines for each kind, and opens a topic when it needs the details.", + "memory.open": "Open my notes", + "memory.builtin.title": "Memory is built in", + "memory.builtin.reason": "Memory is part of LocalAI now, and you do not need this add-on for it. Choose how notes are saved in Settings > Memory.", + "memory.builtin.fix": "Open Settings > Memory", + "memory.save.title": "Menyimpan catatan", + "memory.save.legend": "When the assistant wants to save something to my memory", + "memory.save.ask": "Tanya saya dulu", + "memory.save.ask.desc": "The assistant proposes the note and waits. You confirm it in the chat, or answer yes or no in a messaging app. This is the default.", + "memory.save.auto": "Simpan dan beri tahu saya", + "memory.save.auto.desc": "In your own chats the note is saved at once, and the chat shows \"Saved to memory\" with an Undo. A reply saves at most 5 notes. A long note, or one that looks like it holds a secret, still waits for you.", + "memory.save.never": "Jangan pernah", + "memory.save.never.desc": "The assistant cannot propose a note. It says that saving is off if you ask it to remember something.", + "memory.save.exception": "Chats that come from a messaging app always ask first, whatever you choose here, because the person who wrote the message is not always you. Scheduled and background runs cannot save notes. A saved agent cannot widen your choice: if you choose to be asked, it asks, even when you let it write to memory.", + "memory.save.loading": "Reading your choice", + "memory.save.saving": "Saving your choice", + "memory.save.saved": "Your choice is saved.", + "memory.save.failed": "Your choice could not be saved. The earlier choice is still in force. Try again.", + "memory.save.unreadable": "Your choice could not be read. Reload the page to try again." +} diff --git a/addons/memory/ui/src/i18n/memory.it.json b/addons/memory/ui/src/i18n/memory.it.json new file mode 100644 index 0000000000..b2f789e8d5 --- /dev/null +++ b/addons/memory/ui/src/i18n/memory.it.json @@ -0,0 +1,26 @@ +{ + "memory.title": "Memoria breve", + "memory.intro": "Le tue chat conservano brevi note su di te: dove vivi, cosa ti piace, come vuoi le risposte. Prima di rispondere, il modello legge la nota che corrisponde alla domanda. Non devi chiederglielo.", + "memory.rules.confirm": "Per impostazione predefinita una nuova nota viene salvata solo dopo la tua conferma. Una proposta resta in attesa per un giorno, poi scade. Puoi cambiarlo qui sotto.", + "memory.rules.where": "Le note sono semplici file nella cartella Memoria dei tuoi file. Puoi leggerle, modificarle ed eliminarle lì, oppure nella scheda Memoria del tuo account.", + "memory.rules.scopes": "Ci sono tre tipi di note: le tue per ogni chat, una per ogni agente salvato e una per ogni conversazione che arriva da un'app di messaggistica. Un agente salvato legge le note solo se gli consenti questo add-on.", + "memory.rules.limits": "Il modello legge un breve elenco, al massimo 40 righe per tipo, e apre un argomento quando gli servono i dettagli.", + "memory.open": "Apri le mie note", + "memory.builtin.title": "La memoria è integrata", + "memory.builtin.reason": "La memoria ora fa parte di LocalAI e per usarla non ti serve questo add-on. Scegli come salvare le note in Impostazioni > Memoria.", + "memory.builtin.fix": "Apri Impostazioni > Memoria", + "memory.save.title": "Salvare una nota", + "memory.save.legend": "Quando l'assistente vuole salvare qualcosa nella mia memoria", + "memory.save.ask": "Chiedimi prima", + "memory.save.ask.desc": "L'assistente propone la nota e aspetta. La confermi nella chat, oppure rispondi sì o no in un'app di messaggistica. È l'impostazione predefinita.", + "memory.save.auto": "Salva e avvisami", + "memory.save.auto.desc": "Nelle tue chat la nota viene salvata subito e la chat mostra «Salvato in memoria» con un Annulla. Una risposta salva al massimo 5 note. Una nota lunga, o che sembra contenere un segreto, aspetta comunque te.", + "memory.save.never": "Mai", + "memory.save.never.desc": "L'assistente non può proporre una nota. Se gli chiedi di ricordare qualcosa, dice che il salvataggio è disattivato.", + "memory.save.exception": "Le chat che arrivano da un'app di messaggistica chiedono sempre prima, qualunque cosa scelga qui, perché chi ha scritto il messaggio non sei sempre tu. Le esecuzioni pianificate e in background non possono salvare note. Un agente salvato non può allargare la tua scelta: se scegli di essere interpellato, chiede, anche se gli hai permesso di scrivere in memoria.", + "memory.save.loading": "Lettura della tua scelta", + "memory.save.saving": "Salvataggio della tua scelta", + "memory.save.saved": "La tua scelta è stata salvata.", + "memory.save.failed": "Non è stato possibile salvare la tua scelta. Resta valida la scelta precedente. Riprova.", + "memory.save.unreadable": "Non è stato possibile leggere la tua scelta. Ricarica la pagina per riprovare." +} diff --git a/addons/memory/ui/src/i18n/memory.ko.json b/addons/memory/ui/src/i18n/memory.ko.json new file mode 100644 index 0000000000..e873980b93 --- /dev/null +++ b/addons/memory/ui/src/i18n/memory.ko.json @@ -0,0 +1,26 @@ +{ + "memory.title": "Short memory", + "memory.intro": "Your chats keep short notes about you: where you live, what you like, how you want answers. Before a reply, the model reads the note that matches the question. You do not have to ask it to.", + "memory.rules.confirm": "By default a new note is saved only after you confirm it. A proposal waits for one day, then it expires. You can change this below.", + "memory.rules.where": "The notes are plain files in the Memory folder of your files. You can read, edit and delete them there, or on the Memory tab of your account.", + "memory.rules.scopes": "There are three kinds of notes: yours for every chat, one for each saved agent, and one for each conversation that comes from a messaging app. A saved agent reads notes only when you allow this add-on for it.", + "memory.rules.limits": "The model reads a short list, at most 40 lines for each kind, and opens a topic when it needs the details.", + "memory.open": "Open my notes", + "memory.builtin.title": "Memory is built in", + "memory.builtin.reason": "Memory is part of LocalAI now, and you do not need this add-on for it. Choose how notes are saved in Settings > Memory.", + "memory.builtin.fix": "Open Settings > Memory", + "memory.save.title": "메모 저장", + "memory.save.legend": "When the assistant wants to save something to my memory", + "memory.save.ask": "먼저 물어보기", + "memory.save.ask.desc": "The assistant proposes the note and waits. You confirm it in the chat, or answer yes or no in a messaging app. This is the default.", + "memory.save.auto": "저장하고 알려주기", + "memory.save.auto.desc": "In your own chats the note is saved at once, and the chat shows \"Saved to memory\" with an Undo. A reply saves at most 5 notes. A long note, or one that looks like it holds a secret, still waits for you.", + "memory.save.never": "안 함", + "memory.save.never.desc": "The assistant cannot propose a note. It says that saving is off if you ask it to remember something.", + "memory.save.exception": "Chats that come from a messaging app always ask first, whatever you choose here, because the person who wrote the message is not always you. Scheduled and background runs cannot save notes. A saved agent cannot widen your choice: if you choose to be asked, it asks, even when you let it write to memory.", + "memory.save.loading": "Reading your choice", + "memory.save.saving": "Saving your choice", + "memory.save.saved": "Your choice is saved.", + "memory.save.failed": "Your choice could not be saved. The earlier choice is still in force. Try again.", + "memory.save.unreadable": "Your choice could not be read. Reload the page to try again." +} diff --git a/addons/memory/ui/src/i18n/memory.pt-BR.json b/addons/memory/ui/src/i18n/memory.pt-BR.json new file mode 100644 index 0000000000..4cf705b46e --- /dev/null +++ b/addons/memory/ui/src/i18n/memory.pt-BR.json @@ -0,0 +1,26 @@ +{ + "memory.title": "Short memory", + "memory.intro": "Your chats keep short notes about you: where you live, what you like, how you want answers. Before a reply, the model reads the note that matches the question. You do not have to ask it to.", + "memory.rules.confirm": "By default a new note is saved only after you confirm it. A proposal waits for one day, then it expires. You can change this below.", + "memory.rules.where": "The notes are plain files in the Memory folder of your files. You can read, edit and delete them there, or on the Memory tab of your account.", + "memory.rules.scopes": "There are three kinds of notes: yours for every chat, one for each saved agent, and one for each conversation that comes from a messaging app. A saved agent reads notes only when you allow this add-on for it.", + "memory.rules.limits": "The model reads a short list, at most 40 lines for each kind, and opens a topic when it needs the details.", + "memory.open": "Open my notes", + "memory.builtin.title": "Memory is built in", + "memory.builtin.reason": "Memory is part of LocalAI now, and you do not need this add-on for it. Choose how notes are saved in Settings > Memory.", + "memory.builtin.fix": "Open Settings > Memory", + "memory.save.title": "Salvar uma nota", + "memory.save.legend": "When the assistant wants to save something to my memory", + "memory.save.ask": "Perguntar antes", + "memory.save.ask.desc": "The assistant proposes the note and waits. You confirm it in the chat, or answer yes or no in a messaging app. This is the default.", + "memory.save.auto": "Salvar e me avisar", + "memory.save.auto.desc": "In your own chats the note is saved at once, and the chat shows \"Saved to memory\" with an Undo. A reply saves at most 5 notes. A long note, or one that looks like it holds a secret, still waits for you.", + "memory.save.never": "Nunca", + "memory.save.never.desc": "The assistant cannot propose a note. It says that saving is off if you ask it to remember something.", + "memory.save.exception": "Chats that come from a messaging app always ask first, whatever you choose here, because the person who wrote the message is not always you. Scheduled and background runs cannot save notes. A saved agent cannot widen your choice: if you choose to be asked, it asks, even when you let it write to memory.", + "memory.save.loading": "Reading your choice", + "memory.save.saving": "Saving your choice", + "memory.save.saved": "Your choice is saved.", + "memory.save.failed": "Your choice could not be saved. The earlier choice is still in force. Try again.", + "memory.save.unreadable": "Your choice could not be read. Reload the page to try again." +} diff --git a/addons/memory/ui/src/i18n/memory.zh-CN.json b/addons/memory/ui/src/i18n/memory.zh-CN.json new file mode 100644 index 0000000000..e043e68382 --- /dev/null +++ b/addons/memory/ui/src/i18n/memory.zh-CN.json @@ -0,0 +1,26 @@ +{ + "memory.title": "Short memory", + "memory.intro": "Your chats keep short notes about you: where you live, what you like, how you want answers. Before a reply, the model reads the note that matches the question. You do not have to ask it to.", + "memory.rules.confirm": "By default a new note is saved only after you confirm it. A proposal waits for one day, then it expires. You can change this below.", + "memory.rules.where": "The notes are plain files in the Memory folder of your files. You can read, edit and delete them there, or on the Memory tab of your account.", + "memory.rules.scopes": "There are three kinds of notes: yours for every chat, one for each saved agent, and one for each conversation that comes from a messaging app. A saved agent reads notes only when you allow this add-on for it.", + "memory.rules.limits": "The model reads a short list, at most 40 lines for each kind, and opens a topic when it needs the details.", + "memory.open": "Open my notes", + "memory.builtin.title": "Memory is built in", + "memory.builtin.reason": "Memory is part of LocalAI now, and you do not need this add-on for it. Choose how notes are saved in Settings > Memory.", + "memory.builtin.fix": "Open Settings > Memory", + "memory.save.title": "保存笔记", + "memory.save.legend": "When the assistant wants to save something to my memory", + "memory.save.ask": "先问我", + "memory.save.ask.desc": "The assistant proposes the note and waits. You confirm it in the chat, or answer yes or no in a messaging app. This is the default.", + "memory.save.auto": "保存并告知我", + "memory.save.auto.desc": "In your own chats the note is saved at once, and the chat shows \"Saved to memory\" with an Undo. A reply saves at most 5 notes. A long note, or one that looks like it holds a secret, still waits for you.", + "memory.save.never": "从不", + "memory.save.never.desc": "The assistant cannot propose a note. It says that saving is off if you ask it to remember something.", + "memory.save.exception": "Chats that come from a messaging app always ask first, whatever you choose here, because the person who wrote the message is not always you. Scheduled and background runs cannot save notes. A saved agent cannot widen your choice: if you choose to be asked, it asks, even when you let it write to memory.", + "memory.save.loading": "Reading your choice", + "memory.save.saving": "Saving your choice", + "memory.save.saved": "Your choice is saved.", + "memory.save.failed": "Your choice could not be saved. The earlier choice is still in force. Try again.", + "memory.save.unreadable": "Your choice could not be read. Reload the page to try again." +} diff --git a/addons/memory/ui/src/index.jsx b/addons/memory/ui/src/index.jsx index a3a9ea79b6..bd8b415005 100644 --- a/addons/memory/ui/src/index.jsx +++ b/addons/memory/ui/src/index.jsx @@ -8,6 +8,8 @@ // userSettings, key save-mode); the add-on does not store it and the host // validates it. The rules that make it safe are the host's: this page only // states them. +import { locales } from './i18n/index.js' + export const NOTES_ADDRESS = '/app/account?tab=memory' // Memory is built in; the save level lives in Settings > Memory. export const SETTINGS_ADDRESS = '/app/account?tab=memory' @@ -17,32 +19,7 @@ export const SAVE_MODES = ['ask', 'auto', 'never'] export function createAddon(sdk) { const { React } = sdk const h = React.createElement - sdk.locale.define('en', { - 'memory.title': 'Short memory', - 'memory.intro': 'Your chats keep short notes about you: where you live, what you like, how you want answers. Before a reply, the model reads the note that matches the question. You do not have to ask it to.', - 'memory.rules.confirm': 'By default a new note is saved only after you confirm it. A proposal waits for one day, then it expires. You can change this below.', - 'memory.rules.where': 'The notes are plain files in the Memory folder of your files. You can read, edit and delete them there, or on the Memory tab of your account.', - 'memory.rules.scopes': 'There are three kinds of notes: yours for every chat, one for each saved agent, and one for each conversation that comes from a messaging app. A saved agent reads notes only when you allow this add-on for it.', - 'memory.rules.limits': 'The model reads a short list, at most 40 lines for each kind, and opens a topic when it needs the details.', - 'memory.open': 'Open my notes', - 'memory.builtin.title': 'Memory is built in', - 'memory.builtin.reason': 'Memory is part of LocalAI now, and you do not need this add-on for it. Choose how notes are saved in Settings > Memory.', - 'memory.builtin.fix': 'Open Settings > Memory', - 'memory.save.title': 'Saving a note', - 'memory.save.legend': 'When the assistant wants to save something to my memory', - 'memory.save.ask': 'Ask me first', - 'memory.save.ask.desc': 'The assistant proposes the note and waits. You confirm it in the chat, or answer yes or no in a messaging app. This is the default.', - 'memory.save.auto': 'Save it and tell me', - 'memory.save.auto.desc': 'In your own chats the note is saved at once, and the chat shows "Saved to memory" with an Undo. A reply saves at most 5 notes. A long note, or one that looks like it holds a secret, still waits for you.', - 'memory.save.never': 'Never', - 'memory.save.never.desc': 'The assistant cannot propose a note. It says that saving is off if you ask it to remember something.', - 'memory.save.exception': 'Chats that come from a messaging app always ask first, whatever you choose here, because the person who wrote the message is not always you. Scheduled and background runs cannot save notes. A saved agent cannot widen your choice: if you choose to be asked, it asks, even when you let it write to memory.', - 'memory.save.loading': 'Reading your choice', - 'memory.save.saving': 'Saving your choice', - 'memory.save.saved': 'Your choice is saved.', - 'memory.save.failed': 'Your choice could not be saved. The earlier choice is still in force. Try again.', - 'memory.save.unreadable': 'Your choice could not be read. Reload the page to try again.', - }) + for (const [language, bundle] of Object.entries(locales)) sdk.locale.define(language, bundle) const t = (key) => sdk.locale.t(key, key) // The picker. It shows the choice the host reports, never one it assumed, and diff --git a/addons/memory/ui/test/page.test.mjs b/addons/memory/ui/test/page.test.mjs index 3f1173ded7..ecef0f57d9 100644 --- a/addons/memory/ui/test/page.test.mjs +++ b/addons/memory/ui/test/page.test.mjs @@ -35,3 +35,16 @@ test('page: the button opens the Memory tab of the account', async () => { find(tree).props.onClick() assert.deepEqual(opened, [mod.NOTES_ADDRESS || '/app/account?tab=memory']) }) + +test('page: it reads in the language of the web UI, and in English for a language it has no text for', async () => { + const it = await load({ language: () => 'it' }) + const italian = draw(it.mod.createAddon(it.sdk), { page: 'memory' }) + assert.match(italian, /Memoria breve/) + assert.match(italian, /Apri le mie note/) + assert.doesNotMatch(italian, /Short memory/) + const de = await load({ language: () => 'de' }) + const german = draw(de.mod.createAddon(de.sdk), { page: 'memory' }) + assert.match(german, /Short memory/, 'German repeats the English title until it is translated') + const unknown = await load({ language: () => 'xx' }) + assert.match(draw(unknown.mod.createAddon(unknown.sdk), { page: 'memory' }), /Short memory/) +}) diff --git a/addons/quantization/ui/src/ctx.jsx b/addons/quantization/ui/src/ctx.jsx index be3969d726..f0f9855a8f 100644 --- a/addons/quantization/ui/src/ctx.jsx +++ b/addons/quantization/ui/src/ctx.jsx @@ -1,4 +1,4 @@ -import en from './i18n/en.js' +import { locales } from './i18n/index.js' import { modelFrom as pickModel } from './lib/model.js' // The one object every factory of the quantization add-on receives. It binds the @@ -10,7 +10,9 @@ export function createContext(sdk) { const { React } = sdk const h = React.createElement const Fragment = React.Fragment - sdk.locale.define('en', Object.fromEntries(Object.entries(en).filter(([, v]) => typeof v === 'string'))) + // Every language of the page; a key a language lacks reads in English. + for (const [language, bundle] of Object.entries(locales)) sdk.locale.define(language, bundle) + const en = locales.en const t = (key, vars) => sdk.locale.t(key, key, vars) // The text of a key, or the fallback when the bundle has none. diff --git a/addons/quantization/ui/src/i18n/index.js b/addons/quantization/ui/src/i18n/index.js new file mode 100644 index 0000000000..a4d5c313cf --- /dev/null +++ b/addons/quantization/ui/src/i18n/index.js @@ -0,0 +1,16 @@ +// The words of the quantization pages, one JSON bundle per language of the web UI. +// English is the source and the fallback of every key. The texts repeat the +// host's own tools.json for each language; the few keys the add-on owns +// are drafts that want a native review. addons/internal/i18ncheck +// checks that every bundle has the keys and placeholders of English. +import en from './quantization.en.json' +import it from './quantization.it.json' +import de from './quantization.de.json' +import es from './quantization.es.json' +import fr from './quantization.fr.json' +import id from './quantization.id.json' +import ko from './quantization.ko.json' +import pt_BR from './quantization.pt-BR.json' +import zh_CN from './quantization.zh-CN.json' + +export const locales = { en, it, de, es, fr, id, ko, 'pt-BR': pt_BR, 'zh-CN': zh_CN } diff --git a/addons/quantization/ui/src/i18n/quantization.de.json b/addons/quantization/ui/src/i18n/quantization.de.json new file mode 100644 index 0000000000..ea6f1fb7d8 --- /dev/null +++ b/addons/quantization/ui/src/i18n/quantization.de.json @@ -0,0 +1,180 @@ +{ + "steps.label": "Fortschritt", + "steps.setup": "Einrichten", + "steps.check": "Prüfen", + "steps.run": "Ausführen", + "steps.result": "Ergebnis", + "run.stop": "Stoppen", + "run.connecting": "Verbindung zum Job wird hergestellt. Warten auf das erste Fortschrittsereignis.", + "run.progress": "Fortschritt", + "run.stages": "Phasen", + "run.step": "Schritt", + "run.of": "von", + "run.epoch": "Epoche", + "run.tokens": "Token/s", + "run.eta": "noch etwa {{time}}", + "run.etaSeconds": "noch etwa {{seconds}} s", + "landing.experimental": "Experimentell", + "checks.heading": "Vor dem Start prüfen", + "checks.live": "Wird aktualisiert, während Sie die Einrichtung ändern.", + "checks.meterLabel": "{{used}} Prozent belegt, {{free}} von {{total}} frei", + "checks.openBackends": "Backends öffnen", + "checks.barOk": "Nichts blockiert den Start.", + "checks.barWarn_one": "{{count}} Warnung. Sie können trotzdem starten.", + "checks.barWarn_other": "{{count}} Warnungen. Sie können trotzdem starten.", + "checks.title.model": "Modell", + "checks.title.data": "Datensatz", + "checks.title.backend": "Backend", + "checks.title.memory": "Speicher", + "checks.title.disk": "Festplatte", + "checks.title.reward": "Belohnungsfunktionen", + "checks.title.resume": "Fortsetzen", + "checks.title.access": "Modellzugriff", + "checks.title.type": "Quantisierungstyp", + "checks.title.source": "Quelle", + "checks.title.name": "Name", + "checks.fineTune.modelOk": "Training von {{model}}.", + "checks.fineTune.modelMissing": "Geben Sie das Modell ein, das trainiert werden soll.", + "checks.fineTune.dataOk": "Die Daten kommen von {{source}}.", + "checks.fineTune.dataMissing": "Fügen Sie einen Datensatz hinzu: eine Hugging-Face-ID oder eine hochgeladene Datei.", + "checks.fineTune.backendOk": "{{name}} ist installiert.", + "checks.fineTune.backendMissing": "Kein Backend für Feinabstimmung ist installiert, der Job kann daher nicht starten.", + "checks.fineTune.backendOther": "{{name}} gehört nicht zu den installierten Backends für Feinabstimmung ({{list}}).", + "checks.fineTune.backendUnknown": "Die installierten Backends konnten nicht gelesen werden.", + "checks.fineTune.memoryGpu": "{{name}}: {{free}} von {{total}} frei. Batch-Größe {{batch}}, Sequenzlänge {{seq}}, Gradientenakkumulation {{accum}}. Wie viel der Job braucht, hängt vom Modell und diesen Einstellungen ab. LocalAI schätzt es vor dem Start des Jobs nicht.", + "checks.fineTune.memoryCluster": "{{total}} auf dem besten Knoten ({{node}}). Batch-Größe {{batch}}, Sequenzlänge {{seq}}. Wie viel der Job braucht, hängt vom Modell und diesen Einstellungen ab.", + "checks.fineTune.memoryCpu": "Keine GPU gefunden. Das Training läuft auf der CPU, und {{free}} von {{total}} RAM sind frei. Rechnen Sie mit Langsamkeit. Batch-Größe {{batch}}, Sequenzlänge {{seq}}.", + "checks.fineTune.memoryUnknown": "Dieses Konto kann den Speicher des Rechners nicht lesen. Batch-Größe {{batch}}, Sequenzlänge {{seq}}. Wie viel der Job braucht, hängt vom Modell und diesen Einstellungen ab.", + "checks.fineTune.disk": "{{free}} frei auf dem Modell-Speicher. Downloads und Checkpoints brauchen Platz.", + "checks.fineTune.diskLow": "Nur {{free}} frei auf dem Modell-Speicher. Downloads und Checkpoints brauchen Platz.", + "checks.fineTune.rewardMissing": "GRPO braucht mindestens eine Belohnungsfunktion. Fügen Sie unter Weitere Optionen eine hinzu.", + "checks.fineTune.resume": "Das Training wird ab {{path}} fortgesetzt.", + "checks.fineTune.accessNone": "Kein Token gesetzt. Ein geschütztes Modell braucht ein Hugging-Face-Token unter Weitere Optionen.", + "checks.fineTune.accessSet": "Ein Hugging-Face-Token ist gesetzt.", + "checks.quantize.modelOk": "Quantisierung von {{model}}.", + "checks.quantize.modelMissing": "Geben Sie das Modell ein, das quantisiert werden soll.", + "checks.quantize.typeMissing": "Geben Sie den eigenen Quantisierungstyp ein.", + "checks.quantize.backendOk": "{{name}} ist installiert.", + "checks.quantize.backendMissing": "Kein Quantisierungs-Backend ist installiert, der Job kann daher nicht starten.", + "checks.quantize.backendOther": "{{name}} gehört nicht zu den installierten Quantisierungs-Backends ({{list}}).", + "checks.quantize.backendUnknown": "Die installierten Backends konnten nicht gelesen werden.", + "checks.quantize.memory": "{{free}} von {{total}} RAM sind frei. Die Umwandlung läuft auf der CPU.", + "checks.quantize.disk": "{{free}} frei auf dem Modell-Speicher. Download, umgewandelte Datei und Ergebnis werden auf die Festplatte geschrieben.", + "checks.quantize.diskLow": "Nur {{free}} frei auf dem Modell-Speicher. Download, umgewandelte Datei und Ergebnis werden auf die Festplatte geschrieben.", + "checks.quantize.accessNone": "Kein Token gesetzt. Ein geschütztes Modell braucht ein Hugging-Face-Token unter Weitere Optionen.", + "checks.quantize.accessSet": "Ein Hugging-Face-Token ist gesetzt.", + "checks.import.sourceOk_huggingface": "Erkannt als Hugging-Face-Repository. LocalAI prüft seine Dateien beim Import.", + "checks.import.sourceOk_url": "URL-Format erkannt. Der Inhalt wurde nicht geprüft.", + "checks.import.sourceOk_config": "Erkannt als Konfigurationsdatei eines Modells. Sie wird so gelesen und verwendet, wie sie ist.", + "checks.import.sourceOk_oci": "Erkannt als OCI-Image-Referenz.", + "checks.import.sourceOk_ollama": "Erkannt als Ollama-Modellreferenz.", + "checks.import.sourceOk_local": "Erkannt als Pfad auf dem Rechner, auf dem LocalAI läuft.", + "checks.import.sourceOk": "Die Quelle ist erkannt.", + "checks.import.sourceUnknown": "Das sieht nach keinem Format aus, das LocalAI kennt. Es wird trotzdem versucht und kann fehlschlagen.", + "checks.import.backendDownload": "{{name}} ist noch nicht installiert. LocalAI lädt es zuerst herunter.", + "checks.import.backendChosen": "{{name}} ist ausgewählt und wird installiert.", + "checks.import.backendAuto": "LocalAI wählt das Backend anhand der Dateien der Quelle, sobald der Import beginnt.", + "checks.import.nameTaken": "Ein Modell namens {{name}} ist bereits installiert.", + "checks.import.nameFree": "{{name}} ist nicht in Verwendung.", + "checks.import.nameAuto": "Das Modell erhält den Namen der Quelle.", + "checks.import.disk": "{{free}} frei auf dem Modell-Speicher. Die Download-Größe ist bekannt, sobald der Import beginnt.", + "checks.import.memory_gpu": "{{free}} GPU-Speicher sind jetzt frei. Was dieses Modell braucht, ist bekannt, sobald der Import beginnt.", + "checks.import.memory_ram": "{{free}} RAM sind jetzt frei. Was dieses Modell braucht, ist bekannt, sobald der Import beginnt.", + "checks.import.memory_cluster": "{{free}} sind auf dem besten Knoten verfügbar. Was dieses Modell braucht, ist bekannt, sobald der Import beginnt.", + "checks.import.memory": "{{free}} Speicher sind jetzt frei. Was dieses Modell braucht, ist bekannt, sobald der Import beginnt.", + "form.more": "Weitere Optionen", + "form.backend": "Backend", + "form.token": "Hugging-Face-Token", + "fineTune.starting": "Wird gestartet...", + "fineTune.noMessage": "Das Backend hat keine Meldung ausgegeben.", + "fineTune.tryTitle": "Was als Nächstes zu tun ist", + "fineTune.reuse": "Einrichtung erneut öffnen", + "stop.cancelDelete": "Abbrechen", + "log.title": "Protokoll", + "log.note": "Was der Job seit dem Öffnen dieser Seite gemeldet hat.", + "log.only": "Nur Warnungen und Fehler", + "log.copy": "Kopieren", + "log.copied": "Kopiert", + "log.download": "Herunterladen", + "log.empty": "Noch nichts gemeldet.", + "log.noneFiltered": "Keine Warnungen oder Fehler.", + "jobs.title": "Frühere Jobs", + "jobs.count_one": "{{count}} auf diesem Server gespeichert", + "jobs.count_other": "{{count}} auf diesem Server gespeichert", + "jobs.loading": "Jobs werden geladen", + "jobs.emptyTitle": "Noch keine Feinabstimmungs-Jobs", + "jobs.emptyText": "Richten Sie oben einen ein. Er erscheint hier mit seinem Status, und Sie können seine Einrichtung später wiederverwenden.", + "jobs.model": "Modell", + "jobs.kind": "Art", + "jobs.status": "Status", + "jobs.started": "Gestartet", + "jobs.actions": "Aktionen", + "jobs.open": "Diesen Job öffnen", + "jobs.reuse": "Wiederverwenden", + "jobs.reuseTitle": "Die Einrichtung dieses Jobs für einen neuen Job verwenden", + "jobs.delete": "Löschen", + "jobs.deleteTitle": "Diesen Job und seine Daten löschen", + "jobs.deleteDialogTitle": "Diesen Job löschen?", + "jobs.deleteDialogText": "Dies löscht den Job mit seinen Checkpoints und einem exportierten Modell. Das kann nicht rückgängig gemacht werden.", + "quantize.title": "Quantisieren", + "quantize.lede": "Machen Sie ein Modell kleiner, damit es in weniger Speicher passt. Das Ergebnis ist eine GGUF-Datei, die Sie importieren können.", + "quantize.newJob": "Neuer Job", + "quantize.sectionModel": "Modell", + "quantize.model": "Modell", + "quantize.modelHint": "Eine Hugging-Face-ID oder ein Pfad auf diesem Host.", + "quantize.sectionPrecision": "Genauigkeit", + "quantize.type": "Quantisierungstyp", + "quantize.typeHint": "Eine kleinere Zahl bedeutet eine kleinere Datei und etwas weniger Qualität.", + "quantize.custom": "Eigener...", + "quantize.customLabel": "Eigener Typ", + "quantize.customPlaceholder": "zum Beispiel iq4_nl", + "quantize.moreHint": "Backend, Hugging-Face-Token", + "quantize.tokenHint": "Nur für geschützte Modelle nötig.", + "quantize.recipe": "{{model}} in GGUF mit {{type}} umwandeln.", + "quantize.recipeEmpty": "Geben Sie ein Modell ein, um zu sehen, was erstellt wird.", + "quantize.start": "Quantisieren ({{type}})", + "quantize.barNote": "Nichts wird geändert, bis Sie starten.", + "quantize.jobMeta": "{{type}} auf {{backend}}, gestartet {{started}}", + "quantize.failed": "Quantisierung fehlgeschlagen", + "quantize.finished": "Quantisierung abgeschlossen: {{type}}.", + "quantize.stopped": "Der Job wurde gestoppt.", + "quantize.tryText": "Öffnen Sie die Einrichtung erneut, ändern Sie, worauf die Meldung hinweist, und starten Sie einen neuen Job. Ein neueres Backend unterstützt vielleicht eine Architektur, die ein älteres nicht unterstützt.", + "quantize.outputTitle": "Das Ergebnis", + "quantize.outputText": "Eine GGUF-Datei mit {{type}}. Importieren Sie sie, um sie in LocalAI zu nutzen, oder laden Sie sie herunter.", + "quantize.file": "Datei", + "quantize.importName": "Modellname", + "quantize.importNamePlaceholder": "Wird erzeugt, wenn leer", + "quantize.importBlocked": "Der Import ist auf diesem Server ausgeschaltet, weil er den Modellordner nicht mit dem Add-on-Host teilt.", + "quantize.import": "In LocalAI importieren", + "quantize.importing": "Wird importiert...", + "quantize.importFailed": "Import fehlgeschlagen", + "quantize.importFailedWith": "Import fehlgeschlagen: {{message}}", + "quantize.download": "GGUF herunterladen", + "quantize.nextTitle": "{{name}} ist jetzt ein Modell", + "quantize.nextText": "Es ist bei LocalAI registriert. Öffnen Sie einen Chat damit oder finden Sie es unter Modelle.", + "quantize.chat": "Mit {{name}} chatten", + "quantize.models": "Unter Modelle öffnen", + "quantize.imported": "importiert als {{name}}", + "quantize.emptyTitle": "Noch keine Quantisierungs-Jobs", + "quantize.emptyText": "Richten Sie oben einen ein. Er erscheint hier mit seinem Status.", + "quantize.deleteDialogText": "Dies löscht den Job mit seinen umgewandelten Dateien. Das kann nicht rückgängig gemacht werden.", + "quantize.startFailed": "Job konnte nicht gestartet werden", + "quantize.stopFailed": "Job konnte nicht gestoppt werden", + "quantize.deleteFailed": "Job konnte nicht gelöscht werden", + "quantize.status.queued": "In der Warteschlange", + "quantize.status.downloading": "Wird heruntergeladen", + "quantize.status.converting": "Wird umgewandelt", + "quantize.status.quantizing": "Wird quantisiert", + "quantize.status.completed": "Abgeschlossen", + "quantize.status.failed": "Fehlgeschlagen", + "quantize.status.stopped": "Gestoppt", + "off.badge": "Für Sie nicht verfügbar", + "off.fineTune.title": "Ihr Konto kann keine Feinabstimmung", + "off.fineTune.text": "Feinabstimmung ist eine eigene Berechtigung, und dieses Konto hat sie nicht.", + "off.quantize.title": "Ihr Konto kann nicht quantisieren", + "off.quantize.text": "Quantisierung ist eine eigene Berechtigung, und dieses Konto hat sie nicht.", + "off.admin": "Ein Administrator kann sie unter Benutzer und Schlüssel aktivieren.", + "off.askAdmin": "Bitten Sie einen Administrator, sie für Ihr Konto zu aktivieren.", + "off.back": "Zur Startseite", + "run.unknownPercent": "Fortschritt noch nicht gemeldet" +} diff --git a/addons/quantization/ui/src/i18n/en.js b/addons/quantization/ui/src/i18n/quantization.en.json similarity index 98% rename from addons/quantization/ui/src/i18n/en.js rename to addons/quantization/ui/src/i18n/quantization.en.json index f089fe8a94..ae9c4b15f5 100644 --- a/addons/quantization/ui/src/i18n/en.js +++ b/addons/quantization/ui/src/i18n/quantization.en.json @@ -1,6 +1,4 @@ -// Generated once from the host locale file public/locales/en/tools.json; edit by hand from here on. -// The last key belongs to this add-on. -export default { +{ "steps.label": "Progress", "steps.setup": "Set up", "steps.check": "Check", diff --git a/addons/quantization/ui/src/i18n/quantization.es.json b/addons/quantization/ui/src/i18n/quantization.es.json new file mode 100644 index 0000000000..99cb147e37 --- /dev/null +++ b/addons/quantization/ui/src/i18n/quantization.es.json @@ -0,0 +1,180 @@ +{ + "steps.label": "Progreso", + "steps.setup": "Configurar", + "steps.check": "Comprobar", + "steps.run": "Ejecutar", + "steps.result": "Resultado", + "run.stop": "Detener", + "run.connecting": "Conectando con el job. Esperando el primer evento de progreso.", + "run.progress": "Progreso", + "run.stages": "Etapas", + "run.step": "paso", + "run.of": "de", + "run.epoch": "época", + "run.tokens": "tokens/s", + "run.eta": "faltan unos {{time}}", + "run.etaSeconds": "faltan unos {{seconds}} s", + "landing.experimental": "Experimental", + "checks.heading": "Comprueba antes de empezar", + "checks.live": "Se actualiza mientras cambias la configuración.", + "checks.meterLabel": "{{used}} por ciento en uso, {{free}} libres de {{total}}", + "checks.openBackends": "Abrir Backends", + "checks.barOk": "Nada bloquea el inicio.", + "checks.barWarn_one": "{{count}} aviso. Aun así puedes empezar.", + "checks.barWarn_other": "{{count}} avisos. Aun así puedes empezar.", + "checks.title.model": "Modelo", + "checks.title.data": "Dataset", + "checks.title.backend": "Backend", + "checks.title.memory": "Memoria", + "checks.title.disk": "Disco", + "checks.title.reward": "Funciones de recompensa", + "checks.title.resume": "Reanudar", + "checks.title.access": "Acceso al modelo", + "checks.title.type": "Tipo de cuantización", + "checks.title.source": "Origen", + "checks.title.name": "Nombre", + "checks.fineTune.modelOk": "Entrenando {{model}}.", + "checks.fineTune.modelMissing": "Escribe el modelo que quieres entrenar.", + "checks.fineTune.dataOk": "Los datos vienen de {{source}}.", + "checks.fineTune.dataMissing": "Añade un dataset: un id de Hugging Face o un archivo subido.", + "checks.fineTune.backendOk": "{{name}} está instalado.", + "checks.fineTune.backendMissing": "No hay ningún backend de ajuste instalado, así que el job podría no arrancar.", + "checks.fineTune.backendOther": "{{name}} no está entre los backends de ajuste instalados ({{list}}).", + "checks.fineTune.backendUnknown": "No se pudieron leer los backends instalados.", + "checks.fineTune.memoryGpu": "{{name}}: {{free}} libres de {{total}}. Tamaño de lote {{batch}}, longitud de secuencia {{seq}}, acumulación de gradiente {{accum}}. Cuánta memoria necesita el job depende del modelo y de estos ajustes, y LocalAI no lo estima antes de que el job arranque.", + "checks.fineTune.memoryCluster": "{{total}} en el mejor nodo ({{node}}). Tamaño de lote {{batch}}, longitud de secuencia {{seq}}. Cuánta memoria necesita el job depende del modelo y de estos ajustes.", + "checks.fineTune.memoryCpu": "No se encontró ninguna GPU. El entrenamiento usa la CPU y {{free}} de {{total}} de RAM están libres. Espera que sea lento. Tamaño de lote {{batch}}, longitud de secuencia {{seq}}.", + "checks.fineTune.memoryUnknown": "Esta cuenta no puede leer la memoria de la máquina. Tamaño de lote {{batch}}, longitud de secuencia {{seq}}. Cuánta memoria necesita el job depende del modelo y de estos ajustes.", + "checks.fineTune.disk": "{{free}} libres en el disco de modelos. Las descargas y los checkpoints necesitan espacio.", + "checks.fineTune.diskLow": "Solo {{free}} libres en el disco de modelos. Las descargas y los checkpoints necesitan espacio.", + "checks.fineTune.rewardMissing": "GRPO necesita al menos una función de recompensa. Añade una en Más opciones.", + "checks.fineTune.resume": "El entrenamiento continúa desde {{path}}.", + "checks.fineTune.accessNone": "No hay token definido. Un modelo restringido necesita un token de Hugging Face en Más opciones.", + "checks.fineTune.accessSet": "Hay un token de Hugging Face definido.", + "checks.quantize.modelOk": "Cuantizando {{model}}.", + "checks.quantize.modelMissing": "Escribe el modelo que quieres cuantizar.", + "checks.quantize.typeMissing": "Escribe el tipo de cuantización personalizado.", + "checks.quantize.backendOk": "{{name}} está instalado.", + "checks.quantize.backendMissing": "No hay ningún backend de cuantización instalado, así que el job podría no arrancar.", + "checks.quantize.backendOther": "{{name}} no está entre los backends de cuantización instalados ({{list}}).", + "checks.quantize.backendUnknown": "No se pudieron leer los backends instalados.", + "checks.quantize.memory": "{{free}} libres de {{total}} de RAM. La conversión se ejecuta en la CPU.", + "checks.quantize.disk": "{{free}} libres en el disco de modelos. La descarga, el archivo convertido y el resultado se escriben en disco.", + "checks.quantize.diskLow": "Solo {{free}} libres en el disco de modelos. La descarga, el archivo convertido y el resultado se escriben en disco.", + "checks.quantize.accessNone": "No hay token definido. Un modelo restringido necesita un token de Hugging Face en Más opciones.", + "checks.quantize.accessSet": "Hay un token de Hugging Face definido.", + "checks.import.sourceOk_huggingface": "Se reconoce como repositorio de Hugging Face. LocalAI revisa sus archivos al importar.", + "checks.import.sourceOk_url": "Formato de URL reconocido. Su contenido no se ha comprobado.", + "checks.import.sourceOk_config": "Se reconoce como archivo de configuración de un modelo. Se lee y se usa tal como está.", + "checks.import.sourceOk_oci": "Se reconoce como referencia de imagen OCI.", + "checks.import.sourceOk_ollama": "Se reconoce como referencia de modelo de Ollama.", + "checks.import.sourceOk_local": "Se reconoce como ruta en la máquina que ejecuta LocalAI.", + "checks.import.sourceOk": "El origen se reconoce.", + "checks.import.sourceUnknown": "Esto no parece ninguna forma que LocalAI conozca. Lo intentará igualmente y podría fallar.", + "checks.import.backendDownload": "{{name}} aún no está instalado. LocalAI lo descarga primero.", + "checks.import.backendChosen": "{{name}} está elegido e instalado.", + "checks.import.backendAuto": "LocalAI elige el backend según los archivos del origen cuando empieza la importación.", + "checks.import.nameTaken": "Ya hay instalado un modelo llamado {{name}}.", + "checks.import.nameFree": "{{name}} no está en uso.", + "checks.import.nameAuto": "El modelo toma el nombre del origen.", + "checks.import.disk": "{{free}} libres en el disco de modelos. El tamaño de la descarga se conoce cuando empieza la importación.", + "checks.import.memory_gpu": "Ahora hay {{free}} libres de memoria de GPU. Lo que necesita este modelo se conoce cuando empieza la importación.", + "checks.import.memory_ram": "Ahora hay {{free}} libres de RAM. Lo que necesita este modelo se conoce cuando empieza la importación.", + "checks.import.memory_cluster": "Hay {{free}} disponibles en el mejor nodo. Lo que necesita este modelo se conoce cuando empieza la importación.", + "checks.import.memory": "Ahora hay {{free}} libres de memoria. Lo que necesita este modelo se conoce cuando empieza la importación.", + "form.more": "Más opciones", + "form.backend": "Backend", + "form.token": "Token de Hugging Face", + "fineTune.starting": "Iniciando...", + "fineTune.noMessage": "El backend no dio ningún mensaje.", + "fineTune.tryTitle": "Qué hacer después", + "fineTune.reuse": "Volver a abrir la configuración", + "stop.cancelDelete": "Cancelar", + "log.title": "Registro", + "log.note": "Lo que el job ha informado desde que se abrió esta página.", + "log.only": "Solo avisos y errores", + "log.copy": "Copiar", + "log.copied": "Copiado", + "log.download": "Descargar", + "log.empty": "Aún no se ha informado de nada.", + "log.noneFiltered": "No hay avisos ni errores.", + "jobs.title": "Jobs anteriores", + "jobs.count_one": "{{count}} guardado en este servidor", + "jobs.count_other": "{{count}} guardados en este servidor", + "jobs.loading": "Cargando jobs", + "jobs.emptyTitle": "Aún no hay jobs de ajuste fino", + "jobs.emptyText": "Configura uno arriba. Aparece aquí con su estado, y puedes reutilizar su configuración más adelante.", + "jobs.model": "Modelo", + "jobs.kind": "Tipo", + "jobs.status": "Estado", + "jobs.started": "Iniciado", + "jobs.actions": "Acciones", + "jobs.open": "Abrir este job", + "jobs.reuse": "Reutilizar", + "jobs.reuseTitle": "Usar la configuración de este job para uno nuevo", + "jobs.delete": "Eliminar", + "jobs.deleteTitle": "Eliminar este job y sus datos", + "jobs.deleteDialogTitle": "¿Eliminar este job?", + "jobs.deleteDialogText": "Esto elimina el job con sus checkpoints y cualquier modelo exportado. No se puede deshacer.", + "quantize.title": "Cuantizar", + "quantize.lede": "Haz un modelo más pequeño para que quepa en menos memoria. El resultado es un archivo GGUF que puedes importar.", + "quantize.newJob": "Nuevo job", + "quantize.sectionModel": "Modelo", + "quantize.model": "Modelo", + "quantize.modelHint": "Un id de Hugging Face, o una ruta en este host.", + "quantize.sectionPrecision": "Precisión", + "quantize.type": "Tipo de cuantización", + "quantize.typeHint": "Un número más bajo significa un archivo más pequeño y algo menos de calidad.", + "quantize.custom": "Personalizado...", + "quantize.customLabel": "Tipo personalizado", + "quantize.customPlaceholder": "por ejemplo iq4_nl", + "quantize.moreHint": "backend, token de Hugging Face", + "quantize.tokenHint": "Solo hace falta para los modelos restringidos.", + "quantize.recipe": "Convertir {{model}} a GGUF en {{type}}.", + "quantize.recipeEmpty": "Escribe un modelo para ver qué se creará.", + "quantize.start": "Cuantizar ({{type}})", + "quantize.barNote": "No se cambia nada hasta que inicies.", + "quantize.jobMeta": "{{type}} en {{backend}}, iniciado {{started}}", + "quantize.failed": "La cuantización falló", + "quantize.finished": "Cuantización terminada: {{type}}.", + "quantize.stopped": "El job se detuvo.", + "quantize.tryText": "Vuelve a abrir la configuración, cambia lo que indica el mensaje e inicia un job nuevo. Un backend más reciente puede soportar una arquitectura que uno anterior no soporta.", + "quantize.outputTitle": "El resultado", + "quantize.outputText": "Un archivo GGUF en {{type}}. Impórtalo para usarlo en LocalAI, o descárgalo.", + "quantize.file": "Archivo", + "quantize.importName": "Nombre del modelo", + "quantize.importNamePlaceholder": "Se genera si está vacío", + "quantize.importBlocked": "La importación está desactivada en este servidor porque no comparte la carpeta de modelos con el host del complemento.", + "quantize.import": "Importar a LocalAI", + "quantize.importing": "Importando...", + "quantize.importFailed": "La importación falló", + "quantize.importFailedWith": "La importación falló: {{message}}", + "quantize.download": "Descargar GGUF", + "quantize.nextTitle": "{{name}} ya es un modelo", + "quantize.nextText": "Está registrado en LocalAI. Abre un chat con él o búscalo en Modelos.", + "quantize.chat": "Chatear con {{name}}", + "quantize.models": "Abrir en Modelos", + "quantize.imported": "importado como {{name}}", + "quantize.emptyTitle": "Aún no hay jobs de cuantización", + "quantize.emptyText": "Configura uno arriba. Aparece aquí con su estado.", + "quantize.deleteDialogText": "Esto elimina el job con sus archivos convertidos. No se puede deshacer.", + "quantize.startFailed": "No se pudo iniciar el job", + "quantize.stopFailed": "No se pudo detener el job", + "quantize.deleteFailed": "No se pudo eliminar el job", + "quantize.status.queued": "En cola", + "quantize.status.downloading": "Descargando", + "quantize.status.converting": "Convirtiendo", + "quantize.status.quantizing": "Cuantizando", + "quantize.status.completed": "Completado", + "quantize.status.failed": "Fallido", + "quantize.status.stopped": "Detenido", + "off.badge": "No disponible para ti", + "off.fineTune.title": "Tu cuenta no puede hacer ajuste fino", + "off.fineTune.text": "El ajuste fino es un permiso aparte, y esta cuenta no lo tiene.", + "off.quantize.title": "Tu cuenta no puede cuantizar", + "off.quantize.text": "La cuantización es un permiso aparte, y esta cuenta no lo tiene.", + "off.admin": "Un administrador puede activarlo en Usuarios y claves.", + "off.askAdmin": "Pide a un administrador que lo active para tu cuenta.", + "off.back": "Ir al inicio", + "run.unknownPercent": "Progreso aún no informado" +} diff --git a/addons/quantization/ui/src/i18n/quantization.fr.json b/addons/quantization/ui/src/i18n/quantization.fr.json new file mode 100644 index 0000000000..eb8bd9fe28 --- /dev/null +++ b/addons/quantization/ui/src/i18n/quantization.fr.json @@ -0,0 +1,180 @@ +{ + "steps.label": "Progression", + "steps.setup": "Configuration", + "steps.check": "Vérification", + "steps.run": "Exécution", + "steps.result": "Résultat", + "run.stop": "Arrêter", + "run.connecting": "Connexion au job. En attente du premier événement de progression.", + "run.progress": "Progression", + "run.stages": "Étapes", + "run.step": "étape", + "run.of": "sur", + "run.epoch": "époque", + "run.tokens": "jetons/s", + "run.eta": "environ {{time}} restantes", + "run.etaSeconds": "environ {{seconds}} s restantes", + "landing.experimental": "Expérimental", + "checks.heading": "Vérifier avant de commencer", + "checks.live": "Se met à jour à mesure que vous modifiez la configuration.", + "checks.meterLabel": "{{used}} pour cent utilisés, {{free}} libres sur {{total}}", + "checks.openBackends": "Ouvrir Backends", + "checks.barOk": "Rien ne bloque le démarrage.", + "checks.barWarn_one": "{{count}} avertissement. Vous pouvez tout de même démarrer.", + "checks.barWarn_other": "{{count}} avertissements. Vous pouvez tout de même démarrer.", + "checks.title.model": "Modèle", + "checks.title.data": "Jeu de données", + "checks.title.backend": "Backend", + "checks.title.memory": "Mémoire", + "checks.title.disk": "Disque", + "checks.title.reward": "Fonctions de récompense", + "checks.title.resume": "Reprise", + "checks.title.access": "Accès au modèle", + "checks.title.type": "Type de quantification", + "checks.title.source": "Source", + "checks.title.name": "Nom", + "checks.fineTune.modelOk": "Entraînement de {{model}}.", + "checks.fineTune.modelMissing": "Saisissez le modèle à entraîner.", + "checks.fineTune.dataOk": "Les données viennent de {{source}}.", + "checks.fineTune.dataMissing": "Ajoutez un jeu de données : un identifiant Hugging Face ou un fichier envoyé.", + "checks.fineTune.backendOk": "{{name}} est installé.", + "checks.fineTune.backendMissing": "Aucun backend de fine-tuning n'est installé, la tâche risque donc de ne pas démarrer.", + "checks.fineTune.backendOther": "{{name}} ne fait pas partie des backends de fine-tuning installés ({{list}}).", + "checks.fineTune.backendUnknown": "Impossible de lire les backends installés.", + "checks.fineTune.memoryGpu": "{{name}} : {{free}} libres sur {{total}}. Taille de batch {{batch}}, longueur de séquence {{seq}}, accumulation de gradient {{accum}}. La mémoire nécessaire dépend du modèle et de ces réglages, et LocalAI ne l'estime pas avant le démarrage de la tâche.", + "checks.fineTune.memoryCluster": "{{total}} sur le meilleur nœud ({{node}}). Taille de batch {{batch}}, longueur de séquence {{seq}}. La mémoire nécessaire dépend du modèle et de ces réglages.", + "checks.fineTune.memoryCpu": "Aucun GPU trouvé. L'entraînement utilise le CPU, et {{free}} de RAM sur {{total}} sont libres. Attendez-vous à de la lenteur. Taille de batch {{batch}}, longueur de séquence {{seq}}.", + "checks.fineTune.memoryUnknown": "Ce compte ne peut pas lire la mémoire de la machine. Taille de batch {{batch}}, longueur de séquence {{seq}}. La mémoire nécessaire dépend du modèle et de ces réglages.", + "checks.fineTune.disk": "{{free}} libres sur le disque des modèles. Les téléchargements et les checkpoints ont besoin d'espace.", + "checks.fineTune.diskLow": "Seulement {{free}} libres sur le disque des modèles. Les téléchargements et les checkpoints ont besoin d'espace.", + "checks.fineTune.rewardMissing": "GRPO nécessite au moins une fonction de récompense. Ajoutez-en une dans Plus d'options.", + "checks.fineTune.resume": "L'entraînement reprend depuis {{path}}.", + "checks.fineTune.accessNone": "Aucun jeton défini. Un modèle protégé nécessite un jeton Hugging Face dans Plus d'options.", + "checks.fineTune.accessSet": "Un jeton Hugging Face est défini.", + "checks.quantize.modelOk": "Quantification de {{model}}.", + "checks.quantize.modelMissing": "Saisissez le modèle à quantifier.", + "checks.quantize.typeMissing": "Saisissez le type de quantification personnalisé.", + "checks.quantize.backendOk": "{{name}} est installé.", + "checks.quantize.backendMissing": "Aucun backend de quantification n'est installé, la tâche risque donc de ne pas démarrer.", + "checks.quantize.backendOther": "{{name}} ne fait pas partie des backends de quantification installés ({{list}}).", + "checks.quantize.backendUnknown": "Impossible de lire les backends installés.", + "checks.quantize.memory": "{{free}} de RAM sur {{total}} sont libres. La conversion se fait sur le CPU.", + "checks.quantize.disk": "{{free}} libres sur le disque des modèles. Le téléchargement, le fichier converti et le résultat sont écrits sur le disque.", + "checks.quantize.diskLow": "Seulement {{free}} libres sur le disque des modèles. Le téléchargement, le fichier converti et le résultat sont écrits sur le disque.", + "checks.quantize.accessNone": "Aucun jeton défini. Un modèle protégé nécessite un jeton Hugging Face dans Plus d'options.", + "checks.quantize.accessSet": "Un jeton Hugging Face est défini.", + "checks.import.sourceOk_huggingface": "Reconnu comme dépôt Hugging Face. LocalAI examine ses fichiers lors de l'import.", + "checks.import.sourceOk_url": "Format d'URL reconnu. Son contenu n'a pas été vérifié.", + "checks.import.sourceOk_config": "Reconnu comme fichier de configuration de modèle. Il est lu et utilisé tel quel.", + "checks.import.sourceOk_oci": "Reconnu comme référence d'image OCI.", + "checks.import.sourceOk_ollama": "Reconnu comme référence de modèle Ollama.", + "checks.import.sourceOk_local": "Reconnu comme chemin sur la machine qui exécute LocalAI.", + "checks.import.sourceOk": "La source est reconnue.", + "checks.import.sourceUnknown": "Ce format ne correspond à aucun format connu de LocalAI. L'import essaiera quand même, et peut échouer.", + "checks.import.backendDownload": "{{name}} n'est pas encore installé. LocalAI le télécharge d'abord.", + "checks.import.backendChosen": "{{name}} est choisi et installé.", + "checks.import.backendAuto": "LocalAI choisit le backend d'après les fichiers de la source au démarrage de l'import.", + "checks.import.nameTaken": "Un modèle nommé {{name}} est déjà installé.", + "checks.import.nameFree": "{{name}} n'est pas utilisé.", + "checks.import.nameAuto": "Le modèle prend le nom de la source.", + "checks.import.disk": "{{free}} libres sur le disque des modèles. La taille du téléchargement est connue au démarrage de l'import.", + "checks.import.memory_gpu": "{{free}} de mémoire GPU sont libres maintenant. Ce dont ce modèle a besoin est connu au démarrage de l'import.", + "checks.import.memory_ram": "{{free}} de RAM sont libres maintenant. Ce dont ce modèle a besoin est connu au démarrage de l'import.", + "checks.import.memory_cluster": "{{free}} sont disponibles sur le meilleur nœud. Ce dont ce modèle a besoin est connu au démarrage de l'import.", + "checks.import.memory": "{{free}} de mémoire sont libres maintenant. Ce dont ce modèle a besoin est connu au démarrage de l'import.", + "form.more": "Plus d'options", + "form.backend": "Backend", + "form.token": "Jeton Hugging Face", + "fineTune.starting": "Démarrage...", + "fineTune.noMessage": "Le backend n'a donné aucun message.", + "fineTune.tryTitle": "Que faire ensuite", + "fineTune.reuse": "Rouvrir la configuration", + "stop.cancelDelete": "Annuler", + "log.title": "Journal", + "log.note": "Ce que la tâche a signalé depuis l'ouverture de cette page.", + "log.only": "Avertissements et erreurs uniquement", + "log.copy": "Copier", + "log.copied": "Copié", + "log.download": "Télécharger", + "log.empty": "Rien n'a encore été signalé.", + "log.noneFiltered": "Aucun avertissement ni erreur.", + "jobs.title": "Tâches précédentes", + "jobs.count_one": "{{count}} conservée sur ce serveur", + "jobs.count_other": "{{count}} conservées sur ce serveur", + "jobs.loading": "Chargement des tâches", + "jobs.emptyTitle": "Aucune tâche de fine-tuning pour le moment", + "jobs.emptyText": "Configurez-en une ci-dessus. Elle apparaît ici avec son état, et vous pourrez réutiliser sa configuration plus tard.", + "jobs.model": "Modèle", + "jobs.kind": "Type", + "jobs.status": "État", + "jobs.started": "Démarré", + "jobs.actions": "Actions", + "jobs.open": "Ouvrir cette tâche", + "jobs.reuse": "Réutiliser", + "jobs.reuseTitle": "Utiliser la configuration de cette tâche pour une nouvelle tâche", + "jobs.delete": "Supprimer", + "jobs.deleteTitle": "Supprimer cette tâche et ses données", + "jobs.deleteDialogTitle": "Supprimer cette tâche ?", + "jobs.deleteDialogText": "Cela supprime la tâche avec ses checkpoints et tout modèle exporté. Action irréversible.", + "quantize.title": "Quantifier", + "quantize.lede": "Réduisez la taille d'un modèle pour qu'il tienne dans moins de mémoire. Le résultat est un fichier GGUF que vous pouvez importer.", + "quantize.newJob": "Nouvelle tâche", + "quantize.sectionModel": "Modèle", + "quantize.model": "Modèle", + "quantize.modelHint": "Un identifiant Hugging Face, ou un chemin sur cette machine.", + "quantize.sectionPrecision": "Précision", + "quantize.type": "Type de quantification", + "quantize.typeHint": "Un nombre plus petit signifie un fichier plus petit et une qualité un peu moindre.", + "quantize.custom": "Personnalisé...", + "quantize.customLabel": "Type personnalisé", + "quantize.customPlaceholder": "par exemple iq4_nl", + "quantize.moreHint": "backend, jeton Hugging Face", + "quantize.tokenHint": "Nécessaire uniquement pour les modèles protégés.", + "quantize.recipe": "Convertir {{model}} en GGUF en {{type}}.", + "quantize.recipeEmpty": "Saisissez un modèle pour voir ce qui sera créé.", + "quantize.start": "Quantifier ({{type}})", + "quantize.barNote": "Rien n'est modifié tant que vous ne lancez pas.", + "quantize.jobMeta": "{{type}} sur {{backend}}, démarré {{started}}", + "quantize.failed": "Échec de la quantification", + "quantize.finished": "Quantification terminée : {{type}}.", + "quantize.stopped": "La tâche a été arrêtée.", + "quantize.tryText": "Rouvrez la configuration, modifiez ce que le message indique et lancez une nouvelle tâche. Un backend plus récent peut prendre en charge une architecture que l'ancien ne gère pas.", + "quantize.outputTitle": "Le résultat", + "quantize.outputText": "Un fichier GGUF en {{type}}. Importez-le pour l'utiliser dans LocalAI, ou téléchargez-le.", + "quantize.file": "Fichier", + "quantize.importName": "Nom du modèle", + "quantize.importNamePlaceholder": "Généré si vide", + "quantize.importBlocked": "L'import est désactivé sur ce serveur, car il ne partage pas le dossier des modèles avec l'hôte de l'extension.", + "quantize.import": "Importer dans LocalAI", + "quantize.importing": "Importation...", + "quantize.importFailed": "Échec de l'importation", + "quantize.importFailedWith": "Échec de l'importation : {{message}}", + "quantize.download": "Télécharger le GGUF", + "quantize.nextTitle": "{{name}} est maintenant un modèle", + "quantize.nextText": "Il est enregistré dans LocalAI. Ouvrez une discussion avec lui, ou retrouvez-le dans Modèles.", + "quantize.chat": "Discuter avec {{name}}", + "quantize.models": "Ouvrir dans Modèles", + "quantize.imported": "importé sous le nom {{name}}", + "quantize.emptyTitle": "Aucune tâche de quantification pour le moment", + "quantize.emptyText": "Configurez-en une ci-dessus. Elle apparaît ici avec son état.", + "quantize.deleteDialogText": "Cela supprime la tâche avec ses fichiers convertis. Action irréversible.", + "quantize.startFailed": "Échec du lancement de la tâche", + "quantize.stopFailed": "Échec de l'arrêt de la tâche", + "quantize.deleteFailed": "Échec de la suppression de la tâche", + "quantize.status.queued": "En file d'attente", + "quantize.status.downloading": "Téléchargement", + "quantize.status.converting": "Conversion", + "quantize.status.quantizing": "Quantification", + "quantize.status.completed": "Terminée", + "quantize.status.failed": "Échouée", + "quantize.status.stopped": "Arrêtée", + "off.badge": "Non disponible pour vous", + "off.fineTune.title": "Votre compte ne peut pas faire de fine-tuning", + "off.fineTune.text": "Le fine-tuning est une autorisation distincte, que ce compte n'a pas.", + "off.quantize.title": "Votre compte ne peut pas quantifier", + "off.quantize.text": "La quantification est une autorisation distincte, que ce compte n'a pas.", + "off.admin": "Un administrateur peut l'activer dans Utilisateurs et clés.", + "off.askAdmin": "Demandez à un administrateur de l'activer pour votre compte.", + "off.back": "Aller à l'accueil", + "run.unknownPercent": "Progression pas encore communiquée" +} diff --git a/addons/quantization/ui/src/i18n/quantization.id.json b/addons/quantization/ui/src/i18n/quantization.id.json new file mode 100644 index 0000000000..9345b2638d --- /dev/null +++ b/addons/quantization/ui/src/i18n/quantization.id.json @@ -0,0 +1,180 @@ +{ + "steps.label": "Progres", + "steps.setup": "Siapkan", + "steps.check": "Periksa", + "steps.run": "Jalankan", + "steps.result": "Hasil", + "run.stop": "Hentikan", + "run.connecting": "Menghubungkan ke job. Menunggu event progres pertama.", + "run.progress": "Progres", + "run.stages": "Tahap", + "run.step": "langkah", + "run.of": "dari", + "run.epoch": "epoch", + "run.tokens": "token/s", + "run.eta": "sekitar {{time}} lagi", + "run.etaSeconds": "sekitar {{seconds}} d lagi", + "landing.experimental": "Eksperimental", + "checks.heading": "Periksa sebelum memulai", + "checks.live": "Diperbarui saat Anda mengubah pengaturan.", + "checks.meterLabel": "{{used}} persen terpakai, {{free}} tersedia dari {{total}}", + "checks.openBackends": "Buka Backend", + "checks.barOk": "Tidak ada yang menghalangi permulaan.", + "checks.barWarn_one": "{{count}} peringatan. Anda tetap bisa memulai.", + "checks.barWarn_other": "{{count}} peringatan. Anda tetap bisa memulai.", + "checks.title.model": "Model", + "checks.title.data": "Dataset", + "checks.title.backend": "Backend", + "checks.title.memory": "Memori", + "checks.title.disk": "Disk", + "checks.title.reward": "Fungsi reward", + "checks.title.resume": "Melanjutkan", + "checks.title.access": "Akses model", + "checks.title.type": "Tipe kuantisasi", + "checks.title.source": "Sumber", + "checks.title.name": "Nama", + "checks.fineTune.modelOk": "Melatih {{model}}.", + "checks.fineTune.modelMissing": "Masukkan model yang akan dilatih.", + "checks.fineTune.dataOk": "Data berasal dari {{source}}.", + "checks.fineTune.dataMissing": "Tambahkan dataset: id Hugging Face atau file yang diunggah.", + "checks.fineTune.backendOk": "{{name}} sudah terpasang.", + "checks.fineTune.backendMissing": "Belum ada backend fine-tuning yang terpasang, jadi job mungkin gagal dimulai.", + "checks.fineTune.backendOther": "{{name}} tidak termasuk backend fine-tuning yang terpasang ({{list}}).", + "checks.fineTune.backendUnknown": "Backend yang terpasang tidak dapat dibaca.", + "checks.fineTune.memoryGpu": "{{name}}: {{free}} dari {{total}} tersedia. Ukuran batch {{batch}}, panjang sekuens {{seq}}, akumulasi gradien {{accum}}. Kebutuhan job bergantung pada model dan pengaturan ini, dan LocalAI tidak memperkirakannya sebelum job dimulai.", + "checks.fineTune.memoryCluster": "{{total}} di node terbaik ({{node}}). Ukuran batch {{batch}}, panjang sekuens {{seq}}. Kebutuhan job bergantung pada model dan pengaturan ini.", + "checks.fineTune.memoryCpu": "GPU tidak ditemukan. Pelatihan menggunakan CPU dan {{free}} dari {{total}} RAM tersedia. Diperkirakan lambat. Ukuran batch {{batch}}, panjang sekuens {{seq}}.", + "checks.fineTune.memoryUnknown": "Akun ini tidak dapat membaca memori mesin. Ukuran batch {{batch}}, panjang sekuens {{seq}}. Kebutuhan job bergantung pada model dan pengaturan ini.", + "checks.fineTune.disk": "{{free}} tersedia di disk model. Unduhan dan checkpoint membutuhkan ruang.", + "checks.fineTune.diskLow": "Hanya {{free}} tersedia di disk model. Unduhan dan checkpoint membutuhkan ruang.", + "checks.fineTune.rewardMissing": "GRPO membutuhkan minimal satu fungsi reward. Tambahkan satu di Opsi lainnya.", + "checks.fineTune.resume": "Pelatihan dilanjutkan dari {{path}}.", + "checks.fineTune.accessNone": "Token belum diatur. Model yang dibatasi membutuhkan token Hugging Face di Opsi lainnya.", + "checks.fineTune.accessSet": "Token Hugging Face sudah diatur.", + "checks.quantize.modelOk": "Mengkuantisasi {{model}}.", + "checks.quantize.modelMissing": "Masukkan model yang akan dikuantisasi.", + "checks.quantize.typeMissing": "Masukkan tipe kuantisasi kustom.", + "checks.quantize.backendOk": "{{name}} sudah terpasang.", + "checks.quantize.backendMissing": "Belum ada backend kuantisasi yang terpasang, jadi job mungkin gagal dimulai.", + "checks.quantize.backendOther": "{{name}} tidak termasuk backend kuantisasi yang terpasang ({{list}}).", + "checks.quantize.backendUnknown": "Backend yang terpasang tidak dapat dibaca.", + "checks.quantize.memory": "{{free}} dari {{total}} RAM tersedia. Konversi berjalan di CPU.", + "checks.quantize.disk": "{{free}} tersedia di disk model. Unduhan, file hasil konversi, dan hasilnya ditulis ke disk.", + "checks.quantize.diskLow": "Hanya {{free}} tersedia di disk model. Unduhan, file hasil konversi, dan hasilnya ditulis ke disk.", + "checks.quantize.accessNone": "Token belum diatur. Model yang dibatasi membutuhkan token Hugging Face di Opsi lainnya.", + "checks.quantize.accessSet": "Token Hugging Face sudah diatur.", + "checks.import.sourceOk_huggingface": "Dikenali sebagai repositori Hugging Face. LocalAI memeriksa file-filenya saat Anda mengimpor.", + "checks.import.sourceOk_url": "Format URL dikenali. Isinya belum diperiksa.", + "checks.import.sourceOk_config": "Dikenali sebagai file konfigurasi model. File dibaca dan digunakan apa adanya.", + "checks.import.sourceOk_oci": "Dikenali sebagai referensi image OCI.", + "checks.import.sourceOk_ollama": "Dikenali sebagai referensi model Ollama.", + "checks.import.sourceOk_local": "Dikenali sebagai path di mesin yang menjalankan LocalAI.", + "checks.import.sourceOk": "Sumber dikenali.", + "checks.import.sourceUnknown": "Ini tidak tampak seperti format yang diketahui LocalAI. Tetap akan dicoba, dan mungkin gagal.", + "checks.import.backendDownload": "{{name}} belum terpasang. LocalAI mengunduhnya terlebih dahulu.", + "checks.import.backendChosen": "{{name}} dipilih dan terpasang.", + "checks.import.backendAuto": "LocalAI memilih backend dari file sumber saat impor dimulai.", + "checks.import.nameTaken": "Model bernama {{name}} sudah terpasang.", + "checks.import.nameFree": "{{name}} belum digunakan.", + "checks.import.nameAuto": "Model dinamai sesuai sumbernya.", + "checks.import.disk": "{{free}} tersedia di disk model. Ukuran unduhan diketahui saat impor dimulai.", + "checks.import.memory_gpu": "{{free}} memori GPU tersedia saat ini. Kebutuhan model ini diketahui saat impor dimulai.", + "checks.import.memory_ram": "{{free}} RAM tersedia saat ini. Kebutuhan model ini diketahui saat impor dimulai.", + "checks.import.memory_cluster": "{{free}} tersedia di node terbaik. Kebutuhan model ini diketahui saat impor dimulai.", + "checks.import.memory": "{{free}} memori tersedia saat ini. Kebutuhan model ini diketahui saat impor dimulai.", + "form.more": "Opsi lainnya", + "form.backend": "Backend", + "form.token": "Token Hugging Face", + "fineTune.starting": "Memulai...", + "fineTune.noMessage": "Backend tidak memberikan pesan.", + "fineTune.tryTitle": "Langkah berikutnya", + "fineTune.reuse": "Buka pengaturan lagi", + "stop.cancelDelete": "Batal", + "log.title": "Log", + "log.note": "Yang dilaporkan job sejak halaman ini dibuka.", + "log.only": "Hanya peringatan dan error", + "log.copy": "Salin", + "log.copied": "Disalin", + "log.download": "Unduh", + "log.empty": "Belum ada yang dilaporkan.", + "log.noneFiltered": "Tidak ada peringatan atau error.", + "jobs.title": "Job sebelumnya", + "jobs.count_one": "{{count}} disimpan di server ini", + "jobs.count_other": "{{count}} disimpan di server ini", + "jobs.loading": "Memuat job", + "jobs.emptyTitle": "Belum ada job fine-tuning", + "jobs.emptyText": "Siapkan satu di atas. Job akan muncul di sini dengan statusnya, dan Anda dapat menggunakan kembali pengaturannya nanti.", + "jobs.model": "Model", + "jobs.kind": "Jenis", + "jobs.status": "Status", + "jobs.started": "Dimulai", + "jobs.actions": "Aksi", + "jobs.open": "Buka job ini", + "jobs.reuse": "Gunakan ulang", + "jobs.reuseTitle": "Gunakan pengaturan job ini untuk job baru", + "jobs.delete": "Hapus", + "jobs.deleteTitle": "Hapus job ini beserta datanya", + "jobs.deleteDialogTitle": "Hapus job ini?", + "jobs.deleteDialogText": "Ini menghapus job beserta checkpoint dan model hasil ekspor. Tindakan ini tidak dapat dibatalkan.", + "quantize.title": "Kuantisasi", + "quantize.lede": "Buat model lebih kecil agar muat dalam memori yang lebih sedikit. Hasilnya adalah file GGUF yang dapat Anda impor.", + "quantize.newJob": "Job baru", + "quantize.sectionModel": "Model", + "quantize.model": "Model", + "quantize.modelHint": "Id Hugging Face, atau path di host ini.", + "quantize.sectionPrecision": "Presisi", + "quantize.type": "Tipe kuantisasi", + "quantize.typeHint": "Angka yang lebih kecil berarti file lebih kecil dan kualitas sedikit lebih rendah.", + "quantize.custom": "Kustom...", + "quantize.customLabel": "Tipe kustom", + "quantize.customPlaceholder": "contoh iq4_nl", + "quantize.moreHint": "backend, token Hugging Face", + "quantize.tokenHint": "Hanya dibutuhkan untuk model yang dibatasi.", + "quantize.recipe": "Konversi {{model}} ke GGUF pada {{type}}.", + "quantize.recipeEmpty": "Masukkan model untuk melihat apa yang akan dibuat.", + "quantize.start": "Kuantisasi ({{type}})", + "quantize.barNote": "Tidak ada yang berubah sampai Anda memulai.", + "quantize.jobMeta": "{{type}} pada {{backend}}, dimulai {{started}}", + "quantize.failed": "Kuantisasi gagal", + "quantize.finished": "Kuantisasi selesai: {{type}}.", + "quantize.stopped": "Job dihentikan.", + "quantize.tryText": "Buka pengaturan lagi, ubah apa yang ditunjuk pesan, lalu mulai job baru. Backend yang lebih baru mungkin mendukung arsitektur yang tidak didukung backend lama.", + "quantize.outputTitle": "Hasil", + "quantize.outputText": "File GGUF pada {{type}}. Impor untuk digunakan di LocalAI, atau unduh.", + "quantize.file": "File", + "quantize.importName": "Nama model", + "quantize.importNamePlaceholder": "Dibuat otomatis jika kosong", + "quantize.importBlocked": "Impor dimatikan di server ini karena server tidak berbagi folder model dengan host add-on.", + "quantize.import": "Impor ke LocalAI", + "quantize.importing": "Mengimpor...", + "quantize.importFailed": "Impor gagal", + "quantize.importFailedWith": "Impor gagal: {{message}}", + "quantize.download": "Unduh GGUF", + "quantize.nextTitle": "{{name}} sekarang adalah sebuah model", + "quantize.nextText": "Model sudah terdaftar di LocalAI. Buka chat dengannya, atau temukan di Model.", + "quantize.chat": "Chat dengan {{name}}", + "quantize.models": "Buka di Model", + "quantize.imported": "diimpor sebagai {{name}}", + "quantize.emptyTitle": "Belum ada job kuantisasi", + "quantize.emptyText": "Siapkan satu di atas. Job akan muncul di sini dengan statusnya.", + "quantize.deleteDialogText": "Ini menghapus job beserta file hasil konversinya. Tindakan ini tidak dapat dibatalkan.", + "quantize.startFailed": "Gagal memulai job", + "quantize.stopFailed": "Gagal menghentikan job", + "quantize.deleteFailed": "Gagal menghapus job", + "quantize.status.queued": "Antre", + "quantize.status.downloading": "Mengunduh", + "quantize.status.converting": "Mengonversi", + "quantize.status.quantizing": "Mengkuantisasi", + "quantize.status.completed": "Selesai", + "quantize.status.failed": "Gagal", + "quantize.status.stopped": "Dihentikan", + "off.badge": "Tidak tersedia untuk Anda", + "off.fineTune.title": "Akun Anda tidak dapat melakukan fine-tuning", + "off.fineTune.text": "Fine-tuning adalah izin tersendiri, dan akun ini tidak memilikinya.", + "off.quantize.title": "Akun Anda tidak dapat melakukan kuantisasi", + "off.quantize.text": "Kuantisasi adalah izin tersendiri, dan akun ini tidak memilikinya.", + "off.admin": "Administrator dapat mengaktifkannya di Pengguna dan kunci.", + "off.askAdmin": "Minta administrator untuk mengaktifkannya untuk akun Anda.", + "off.back": "Ke Beranda", + "run.unknownPercent": "Kemajuan belum dilaporkan" +} diff --git a/addons/quantization/ui/src/i18n/quantization.it.json b/addons/quantization/ui/src/i18n/quantization.it.json new file mode 100644 index 0000000000..4d75f46fa6 --- /dev/null +++ b/addons/quantization/ui/src/i18n/quantization.it.json @@ -0,0 +1,180 @@ +{ + "steps.label": "Avanzamento", + "steps.setup": "Configura", + "steps.check": "Verifica", + "steps.run": "Esecuzione", + "steps.result": "Risultato", + "run.stop": "Ferma", + "run.connecting": "Connessione al job. In attesa del primo evento di avanzamento.", + "run.progress": "Avanzamento", + "run.stages": "Fasi", + "run.step": "passo", + "run.of": "di", + "run.epoch": "epoca", + "run.tokens": "token/s", + "run.eta": "circa {{time}} rimanenti", + "run.etaSeconds": "circa {{seconds}} s rimanenti", + "landing.experimental": "Sperimentale", + "checks.heading": "Verifica prima di iniziare", + "checks.live": "Si aggiorna mentre cambi la configurazione.", + "checks.meterLabel": "{{used}} per cento in uso, {{free}} liberi di {{total}}", + "checks.openBackends": "Apri Backend", + "checks.barOk": "Nulla blocca l'avvio.", + "checks.barWarn_one": "{{count}} avviso. Puoi comunque avviare.", + "checks.barWarn_other": "{{count}} avvisi. Puoi comunque avviare.", + "checks.title.model": "Modello", + "checks.title.data": "Dataset", + "checks.title.backend": "Backend", + "checks.title.memory": "Memoria", + "checks.title.disk": "Disco", + "checks.title.reward": "Funzioni di ricompensa", + "checks.title.resume": "Ripresa", + "checks.title.access": "Accesso al modello", + "checks.title.type": "Tipo di quantizzazione", + "checks.title.source": "Sorgente", + "checks.title.name": "Nome", + "checks.fineTune.modelOk": "Addestramento di {{model}}.", + "checks.fineTune.modelMissing": "Inserisci il modello da addestrare.", + "checks.fineTune.dataOk": "I dati arrivano da {{source}}.", + "checks.fineTune.dataMissing": "Aggiungi un dataset: un id Hugging Face o un file caricato.", + "checks.fineTune.backendOk": "{{name}} è installato.", + "checks.fineTune.backendMissing": "Nessun backend di fine-tuning è installato, quindi il job potrebbe non partire.", + "checks.fineTune.backendOther": "{{name}} non è tra i backend di fine-tuning installati ({{list}}).", + "checks.fineTune.backendUnknown": "Impossibile leggere i backend installati.", + "checks.fineTune.memoryGpu": "{{name}}: {{free}} liberi di {{total}}. Batch size {{batch}}, lunghezza della sequenza {{seq}}, accumulo del gradiente {{accum}}. Quanta memoria serva al job dipende dal modello e da queste impostazioni, e LocalAI non la stima prima dell'avvio del job.", + "checks.fineTune.memoryCluster": "{{total}} sul nodo migliore ({{node}}). Batch size {{batch}}, lunghezza della sequenza {{seq}}. Quanta memoria serva al job dipende dal modello e da queste impostazioni.", + "checks.fineTune.memoryCpu": "Nessuna GPU trovata. L'addestramento usa la CPU e {{free}} di RAM su {{total}} sono liberi. Aspettati lentezza. Batch size {{batch}}, lunghezza della sequenza {{seq}}.", + "checks.fineTune.memoryUnknown": "Questo account non può leggere la memoria della macchina. Batch size {{batch}}, lunghezza della sequenza {{seq}}. Quanta memoria serva al job dipende dal modello e da queste impostazioni.", + "checks.fineTune.disk": "{{free}} liberi sul disco dei modelli. Download e checkpoint richiedono spazio.", + "checks.fineTune.diskLow": "Solo {{free}} liberi sul disco dei modelli. Download e checkpoint richiedono spazio.", + "checks.fineTune.rewardMissing": "GRPO richiede almeno una funzione di ricompensa. Aggiungine una in Altre opzioni.", + "checks.fineTune.resume": "L'addestramento continua da {{path}}.", + "checks.fineTune.accessNone": "Nessun token impostato. Un modello con accesso limitato richiede un token Hugging Face in Altre opzioni.", + "checks.fineTune.accessSet": "È impostato un token Hugging Face.", + "checks.quantize.modelOk": "Quantizzazione di {{model}}.", + "checks.quantize.modelMissing": "Inserisci il modello da quantizzare.", + "checks.quantize.typeMissing": "Inserisci il tipo di quantizzazione personalizzato.", + "checks.quantize.backendOk": "{{name}} è installato.", + "checks.quantize.backendMissing": "Nessun backend di quantizzazione è installato, quindi il job potrebbe non partire.", + "checks.quantize.backendOther": "{{name}} non è tra i backend di quantizzazione installati ({{list}}).", + "checks.quantize.backendUnknown": "Impossibile leggere i backend installati.", + "checks.quantize.memory": "{{free}} di RAM su {{total}} sono liberi. La conversione gira sulla CPU.", + "checks.quantize.disk": "{{free}} liberi sul disco dei modelli. Download, file convertito e risultato vengono scritti su disco.", + "checks.quantize.diskLow": "Solo {{free}} liberi sul disco dei modelli. Download, file convertito e risultato vengono scritti su disco.", + "checks.quantize.accessNone": "Nessun token impostato. Un modello con accesso limitato richiede un token Hugging Face in Altre opzioni.", + "checks.quantize.accessSet": "È impostato un token Hugging Face.", + "checks.import.sourceOk_huggingface": "Riconosciuto come repository Hugging Face. LocalAI controlla i suoi file quando importi.", + "checks.import.sourceOk_url": "Formato URL riconosciuto. Il contenuto non è stato verificato.", + "checks.import.sourceOk_config": "Riconosciuto come file di configurazione del modello. Viene letto e usato così com'è.", + "checks.import.sourceOk_oci": "Riconosciuto come riferimento a un'immagine OCI.", + "checks.import.sourceOk_ollama": "Riconosciuto come riferimento a un modello Ollama.", + "checks.import.sourceOk_local": "Riconosciuto come percorso sulla macchina che esegue LocalAI.", + "checks.import.sourceOk": "La sorgente è riconosciuta.", + "checks.import.sourceUnknown": "Non sembra un formato noto a LocalAI. Ci proverà comunque, e potrebbe non riuscire.", + "checks.import.backendDownload": "{{name}} non è ancora installato. LocalAI lo scarica prima.", + "checks.import.backendChosen": "{{name}} è scelto e installato.", + "checks.import.backendAuto": "LocalAI sceglie il backend dai file della sorgente quando l'importazione inizia.", + "checks.import.nameTaken": "Un modello chiamato {{name}} è già installato.", + "checks.import.nameFree": "{{name}} non è in uso.", + "checks.import.nameAuto": "Il modello prende il nome dalla sorgente.", + "checks.import.disk": "{{free}} liberi sul disco dei modelli. La dimensione del download è nota quando l'importazione inizia.", + "checks.import.memory_gpu": "{{free}} di memoria GPU sono liberi ora. Quanta memoria serva a questo modello è noto quando l'importazione inizia.", + "checks.import.memory_ram": "{{free}} di RAM sono liberi ora. Quanta memoria serva a questo modello è noto quando l'importazione inizia.", + "checks.import.memory_cluster": "{{free}} sono disponibili sul nodo migliore. Quanta memoria serva a questo modello è noto quando l'importazione inizia.", + "checks.import.memory": "{{free}} di memoria sono liberi ora. Quanta memoria serva a questo modello è noto quando l'importazione inizia.", + "form.more": "Altre opzioni", + "form.backend": "Backend", + "form.token": "Token Hugging Face", + "fineTune.starting": "Avvio in corso...", + "fineTune.noMessage": "Il backend non ha dato alcun messaggio.", + "fineTune.tryTitle": "Cosa fare dopo", + "fineTune.reuse": "Apri di nuovo la configurazione", + "stop.cancelDelete": "Annulla", + "log.title": "Log", + "log.note": "Ciò che il job ha riportato da quando è aperta questa pagina.", + "log.only": "Solo avvisi ed errori", + "log.copy": "Copia", + "log.copied": "Copiato", + "log.download": "Scarica", + "log.empty": "Ancora nulla da riportare.", + "log.noneFiltered": "Nessun avviso o errore.", + "jobs.title": "Job precedenti", + "jobs.count_one": "{{count}} conservato su questo server", + "jobs.count_other": "{{count}} conservati su questo server", + "jobs.loading": "Caricamento dei job", + "jobs.emptyTitle": "Ancora nessun job di fine-tuning", + "jobs.emptyText": "Configurane uno sopra. Compare qui con il suo stato, e in seguito puoi riusarne la configurazione.", + "jobs.model": "Modello", + "jobs.kind": "Tipo", + "jobs.status": "Stato", + "jobs.started": "Avviato", + "jobs.actions": "Azioni", + "jobs.open": "Apri questo job", + "jobs.reuse": "Riusa", + "jobs.reuseTitle": "Usa la configurazione di questo job per un nuovo job", + "jobs.delete": "Elimina", + "jobs.deleteTitle": "Elimina questo job e i suoi dati", + "jobs.deleteDialogTitle": "Eliminare questo job?", + "jobs.deleteDialogText": "Questo elimina il job con i suoi checkpoint e l'eventuale modello esportato. Non può essere annullato.", + "quantize.title": "Quantizza", + "quantize.lede": "Rendi un modello più piccolo, così occupa meno memoria. Il risultato è un file GGUF che puoi importare.", + "quantize.newJob": "Nuovo job", + "quantize.sectionModel": "Modello", + "quantize.model": "Modello", + "quantize.modelHint": "Un id Hugging Face, o un percorso su questo host.", + "quantize.sectionPrecision": "Precisione", + "quantize.type": "Tipo di quantizzazione", + "quantize.typeHint": "Un numero più piccolo significa un file più piccolo e una qualità leggermente inferiore.", + "quantize.custom": "Personalizzato...", + "quantize.customLabel": "Tipo personalizzato", + "quantize.customPlaceholder": "per esempio iq4_nl", + "quantize.moreHint": "backend, token Hugging Face", + "quantize.tokenHint": "Serve solo per i modelli con accesso limitato.", + "quantize.recipe": "Converti {{model}} in GGUF a {{type}}.", + "quantize.recipeEmpty": "Inserisci un modello per vedere cosa verrà creato.", + "quantize.start": "Quantizza ({{type}})", + "quantize.barNote": "Non cambia nulla finché non avvii.", + "quantize.jobMeta": "{{type}} su {{backend}}, avviato {{started}}", + "quantize.failed": "Quantizzazione non riuscita", + "quantize.finished": "Quantizzazione completata: {{type}}.", + "quantize.stopped": "Il job è stato fermato.", + "quantize.tryText": "Apri di nuovo la configurazione, cambia ciò che indica il messaggio e avvia un nuovo job. Un backend più recente può supportare un'architettura che uno più vecchio non supporta.", + "quantize.outputTitle": "Il risultato", + "quantize.outputText": "Un file GGUF a {{type}}. Importalo per usarlo in LocalAI, oppure scaricalo.", + "quantize.file": "File", + "quantize.importName": "Nome del modello", + "quantize.importNamePlaceholder": "Generato se vuoto", + "quantize.importBlocked": "L'importazione è disattivata su questo server perché non condivide la cartella dei modelli con l'host dell'add-on.", + "quantize.import": "Importa in LocalAI", + "quantize.importing": "Importazione...", + "quantize.importFailed": "Importazione non riuscita", + "quantize.importFailedWith": "Importazione non riuscita: {{message}}", + "quantize.download": "Scarica GGUF", + "quantize.nextTitle": "{{name}} è ora un modello", + "quantize.nextText": "È registrato in LocalAI. Apri una chat con esso, oppure trovalo in Modelli.", + "quantize.chat": "Chatta con {{name}}", + "quantize.models": "Apri in Modelli", + "quantize.imported": "importato come {{name}}", + "quantize.emptyTitle": "Ancora nessun job di quantizzazione", + "quantize.emptyText": "Configurane uno sopra. Compare qui con il suo stato.", + "quantize.deleteDialogText": "Questo elimina il job con i file convertiti. Non può essere annullato.", + "quantize.startFailed": "Impossibile avviare il job", + "quantize.stopFailed": "Impossibile fermare il job", + "quantize.deleteFailed": "Impossibile eliminare il job", + "quantize.status.queued": "In coda", + "quantize.status.downloading": "Download in corso", + "quantize.status.converting": "Conversione", + "quantize.status.quantizing": "Quantizzazione", + "quantize.status.completed": "Completato", + "quantize.status.failed": "Non riuscito", + "quantize.status.stopped": "Fermato", + "off.badge": "Non disponibile per te", + "off.fineTune.title": "Il tuo account non può fare fine-tuning", + "off.fineTune.text": "Il fine-tuning è un permesso separato, e questo account non ce l'ha.", + "off.quantize.title": "Il tuo account non può quantizzare", + "off.quantize.text": "La quantizzazione è un permesso separato, e questo account non ce l'ha.", + "off.admin": "Un amministratore può attivarlo da Utenti e chiavi.", + "off.askAdmin": "Chiedi a un amministratore di attivarlo per il tuo account.", + "off.back": "Vai alla Home", + "run.unknownPercent": "Avanzamento non ancora comunicato" +} diff --git a/addons/quantization/ui/src/i18n/quantization.ko.json b/addons/quantization/ui/src/i18n/quantization.ko.json new file mode 100644 index 0000000000..1e6d4e6833 --- /dev/null +++ b/addons/quantization/ui/src/i18n/quantization.ko.json @@ -0,0 +1,180 @@ +{ + "steps.label": "진행 상황", + "steps.setup": "설정", + "steps.check": "확인", + "steps.run": "실행", + "steps.result": "결과", + "run.stop": "중지", + "run.connecting": "작업에 연결하는 중입니다. 첫 진행 이벤트를 기다리고 있습니다.", + "run.progress": "진행률", + "run.stages": "단계", + "run.step": "스텝", + "run.of": "/", + "run.epoch": "에포크", + "run.tokens": "tokens/s", + "run.eta": "약 {{time}} 남음", + "run.etaSeconds": "약 {{seconds}}초 남음", + "landing.experimental": "실험적", + "checks.heading": "시작하기 전에 확인", + "checks.live": "설정을 바꿀 때마다 업데이트됩니다.", + "checks.meterLabel": "{{used}}% 사용 중, {{total}} 중 {{free}} 사용 가능", + "checks.openBackends": "백엔드 열기", + "checks.barOk": "시작을 막는 항목이 없습니다.", + "checks.barWarn_one": "경고 {{count}}개. 그래도 시작할 수 있습니다.", + "checks.barWarn_other": "경고 {{count}}개. 그래도 시작할 수 있습니다.", + "checks.title.model": "모델", + "checks.title.data": "데이터셋", + "checks.title.backend": "백엔드", + "checks.title.memory": "메모리", + "checks.title.disk": "디스크", + "checks.title.reward": "보상 함수", + "checks.title.resume": "재개", + "checks.title.access": "모델 접근", + "checks.title.type": "양자화 유형", + "checks.title.source": "소스", + "checks.title.name": "이름", + "checks.fineTune.modelOk": "{{model}} 학습 중입니다.", + "checks.fineTune.modelMissing": "학습할 모델을 입력하세요.", + "checks.fineTune.dataOk": "데이터는 {{source}}에서 옵니다.", + "checks.fineTune.dataMissing": "데이터셋을 추가하세요: Hugging Face id 또는 업로드한 파일.", + "checks.fineTune.backendOk": "{{name}}이(가) 설치되어 있습니다.", + "checks.fineTune.backendMissing": "파인튜닝 백엔드가 설치되어 있지 않아 작업이 시작되지 않을 수 있습니다.", + "checks.fineTune.backendOther": "{{name}}은(는) 설치된 파인튜닝 백엔드({{list}}) 중에 없습니다.", + "checks.fineTune.backendUnknown": "설치된 백엔드를 읽을 수 없습니다.", + "checks.fineTune.memoryGpu": "{{name}}: {{total}} 중 {{free}} 사용 가능. 배치 크기 {{batch}}, 시퀀스 길이 {{seq}}, 그래디언트 누적 {{accum}}. 작업에 필요한 양은 모델과 이 설정에 따라 다르며, LocalAI는 작업 시작 전에 이를 추정하지 않습니다.", + "checks.fineTune.memoryCluster": "최적 노드({{node}})의 {{total}}. 배치 크기 {{batch}}, 시퀀스 길이 {{seq}}. 작업에 필요한 양은 모델과 이 설정에 따라 다릅니다.", + "checks.fineTune.memoryCpu": "GPU를 찾을 수 없습니다. 학습은 CPU를 사용하며 RAM {{total}} 중 {{free}}가 사용 가능합니다. 느릴 것으로 예상됩니다. 배치 크기 {{batch}}, 시퀀스 길이 {{seq}}.", + "checks.fineTune.memoryUnknown": "이 계정은 컴퓨터의 메모리를 읽을 수 없습니다. 배치 크기 {{batch}}, 시퀀스 길이 {{seq}}. 작업에 필요한 양은 모델과 이 설정에 따라 다릅니다.", + "checks.fineTune.disk": "모델 디스크에 {{free}} 사용 가능. 다운로드와 체크포인트에 공간이 필요합니다.", + "checks.fineTune.diskLow": "모델 디스크에 {{free}}만 사용 가능. 다운로드와 체크포인트에 공간이 필요합니다.", + "checks.fineTune.rewardMissing": "GRPO에는 보상 함수가 최소 하나 필요합니다. 추가 옵션에서 하나를 추가하세요.", + "checks.fineTune.resume": "학습이 {{path}}에서 이어집니다.", + "checks.fineTune.accessNone": "토큰이 설정되지 않았습니다. 접근이 제한된 모델은 추가 옵션에서 Hugging Face 토큰이 필요합니다.", + "checks.fineTune.accessSet": "Hugging Face 토큰이 설정되어 있습니다.", + "checks.quantize.modelOk": "{{model}}을(를) 양자화합니다.", + "checks.quantize.modelMissing": "양자화할 모델을 입력하세요.", + "checks.quantize.typeMissing": "사용자 지정 양자화 유형을 입력하세요.", + "checks.quantize.backendOk": "{{name}}이(가) 설치되어 있습니다.", + "checks.quantize.backendMissing": "양자화 백엔드가 설치되어 있지 않아 작업이 시작되지 않을 수 있습니다.", + "checks.quantize.backendOther": "{{name}}은(는) 설치된 양자화 백엔드({{list}}) 중에 없습니다.", + "checks.quantize.backendUnknown": "설치된 백엔드를 읽을 수 없습니다.", + "checks.quantize.memory": "RAM {{total}} 중 {{free}}가 사용 가능합니다. 변환은 CPU에서 실행됩니다.", + "checks.quantize.disk": "모델 디스크에 {{free}} 사용 가능. 다운로드, 변환된 파일, 결과가 디스크에 기록됩니다.", + "checks.quantize.diskLow": "모델 디스크에 {{free}}만 사용 가능. 다운로드, 변환된 파일, 결과가 디스크에 기록됩니다.", + "checks.quantize.accessNone": "토큰이 설정되지 않았습니다. 접근이 제한된 모델은 추가 옵션에서 Hugging Face 토큰이 필요합니다.", + "checks.quantize.accessSet": "Hugging Face 토큰이 설정되어 있습니다.", + "checks.import.sourceOk_huggingface": "Hugging Face 저장소로 인식되었습니다. 가져올 때 LocalAI가 파일을 확인합니다.", + "checks.import.sourceOk_url": "URL 형식이 인식되었습니다. 내용은 확인하지 않았습니다.", + "checks.import.sourceOk_config": "모델 구성 파일로 인식되었습니다. 읽은 뒤 있는 그대로 사용합니다.", + "checks.import.sourceOk_oci": "OCI 이미지 참조로 인식되었습니다.", + "checks.import.sourceOk_ollama": "Ollama 모델 참조로 인식되었습니다.", + "checks.import.sourceOk_local": "LocalAI를 실행하는 컴퓨터의 경로로 인식되었습니다.", + "checks.import.sourceOk": "소스가 인식되었습니다.", + "checks.import.sourceUnknown": "LocalAI가 아는 형식이 아닌 것 같습니다. 그래도 시도하며, 실패할 수 있습니다.", + "checks.import.backendDownload": "{{name}}이(가) 아직 설치되어 있지 않습니다. LocalAI가 먼저 다운로드합니다.", + "checks.import.backendChosen": "{{name}}이(가) 선택되어 설치되어 있습니다.", + "checks.import.backendAuto": "가져오기가 시작되면 LocalAI가 소스의 파일을 보고 백엔드를 고릅니다.", + "checks.import.nameTaken": "{{name}}이라는 모델이 이미 설치되어 있습니다.", + "checks.import.nameFree": "{{name}}은(는) 사용 중이 아닙니다.", + "checks.import.nameAuto": "모델은 소스 이름을 따라 명명됩니다.", + "checks.import.disk": "모델 디스크에 {{free}} 사용 가능. 다운로드 크기는 가져오기가 시작되면 알 수 있습니다.", + "checks.import.memory_gpu": "지금 GPU 메모리 {{free}}이(가) 사용 가능합니다. 이 모델에 필요한 양은 가져오기가 시작되면 알 수 있습니다.", + "checks.import.memory_ram": "지금 RAM {{free}}이(가) 사용 가능합니다. 이 모델에 필요한 양은 가져오기가 시작되면 알 수 있습니다.", + "checks.import.memory_cluster": "최적 노드에 {{free}}이(가) 사용 가능합니다. 이 모델에 필요한 양은 가져오기가 시작되면 알 수 있습니다.", + "checks.import.memory": "지금 메모리 {{free}}이(가) 사용 가능합니다. 이 모델에 필요한 양은 가져오기가 시작되면 알 수 있습니다.", + "form.more": "추가 옵션", + "form.backend": "백엔드", + "form.token": "Hugging Face 토큰", + "fineTune.starting": "시작 중...", + "fineTune.noMessage": "백엔드가 메시지를 보내지 않았습니다.", + "fineTune.tryTitle": "다음에 할 일", + "fineTune.reuse": "설정 다시 열기", + "stop.cancelDelete": "취소", + "log.title": "로그", + "log.note": "이 페이지가 열린 뒤 작업이 보고한 내용입니다.", + "log.only": "경고와 오류만", + "log.copy": "복사", + "log.copied": "복사됨", + "log.download": "다운로드", + "log.empty": "아직 보고된 내용이 없습니다.", + "log.noneFiltered": "경고나 오류가 없습니다.", + "jobs.title": "이전 작업", + "jobs.count_one": "이 서버에 {{count}}개 보관됨", + "jobs.count_other": "이 서버에 {{count}}개 보관됨", + "jobs.loading": "작업 불러오는 중", + "jobs.emptyTitle": "아직 파인튜닝 작업이 없습니다", + "jobs.emptyText": "위에서 작업을 설정하세요. 작업은 상태와 함께 여기에 나타나며, 나중에 설정을 재사용할 수 있습니다.", + "jobs.model": "모델", + "jobs.kind": "종류", + "jobs.status": "상태", + "jobs.started": "시작 시간", + "jobs.actions": "작업", + "jobs.open": "이 작업 열기", + "jobs.reuse": "재사용", + "jobs.reuseTitle": "이 작업의 설정을 새 작업에 사용", + "jobs.delete": "삭제", + "jobs.deleteTitle": "이 작업과 데이터 삭제", + "jobs.deleteDialogTitle": "이 작업을 삭제할까요?", + "jobs.deleteDialogText": "작업을 체크포인트와 내보낸 모델과 함께 삭제합니다. 이 작업은 되돌릴 수 없습니다.", + "quantize.title": "양자화", + "quantize.lede": "모델을 더 작게 만들어 더 적은 메모리에 맞춥니다. 결과는 가져올 수 있는 GGUF 파일입니다.", + "quantize.newJob": "새 작업", + "quantize.sectionModel": "모델", + "quantize.model": "모델", + "quantize.modelHint": "Hugging Face id 또는 이 호스트의 경로.", + "quantize.sectionPrecision": "정밀도", + "quantize.type": "양자화 유형", + "quantize.typeHint": "숫자가 작을수록 파일이 작아지고 품질이 조금 떨어집니다.", + "quantize.custom": "사용자 지정...", + "quantize.customLabel": "사용자 지정 유형", + "quantize.customPlaceholder": "예: iq4_nl", + "quantize.moreHint": "백엔드, Hugging Face 토큰", + "quantize.tokenHint": "접근이 제한된 모델에만 필요합니다.", + "quantize.recipe": "{{model}}을(를) {{type}} GGUF로 변환합니다.", + "quantize.recipeEmpty": "모델을 입력하면 만들어질 결과를 볼 수 있습니다.", + "quantize.start": "양자화 ({{type}})", + "quantize.barNote": "시작하기 전까지는 아무것도 바뀌지 않습니다.", + "quantize.jobMeta": "{{backend}}의 {{type}}, 시작 {{started}}", + "quantize.failed": "양자화 실패", + "quantize.finished": "양자화 완료: {{type}}.", + "quantize.stopped": "작업이 중지되었습니다.", + "quantize.tryText": "설정을 다시 열고, 메시지가 가리키는 항목을 바꾼 뒤 새 작업을 시작하세요. 새 백엔드는 오래된 백엔드가 지원하지 않는 아키텍처를 지원할 수 있습니다.", + "quantize.outputTitle": "결과", + "quantize.outputText": "{{type}}의 GGUF 파일입니다. LocalAI에서 쓰려면 가져오거나 다운로드하세요.", + "quantize.file": "파일", + "quantize.importName": "모델 이름", + "quantize.importNamePlaceholder": "비워 두면 자동 생성", + "quantize.importBlocked": "이 서버는 애드온 호스트와 모델 폴더를 공유하지 않으므로 가져오기가 꺼져 있습니다.", + "quantize.import": "LocalAI로 가져오기", + "quantize.importing": "가져오는 중...", + "quantize.importFailed": "가져오기 실패", + "quantize.importFailedWith": "가져오기 실패: {{message}}", + "quantize.download": "GGUF 다운로드", + "quantize.nextTitle": "{{name}}이(가) 이제 모델입니다", + "quantize.nextText": "LocalAI에 등록되었습니다. 이 모델과 채팅을 열거나 모델 페이지에서 찾으세요.", + "quantize.chat": "{{name}}과(와) 채팅", + "quantize.models": "모델에서 열기", + "quantize.imported": "{{name}}(으)로 가져왔습니다", + "quantize.emptyTitle": "아직 양자화 작업이 없습니다", + "quantize.emptyText": "위에서 작업을 설정하세요. 작업은 상태와 함께 여기에 나타납니다.", + "quantize.deleteDialogText": "작업을 변환된 파일과 함께 삭제합니다. 이 작업은 되돌릴 수 없습니다.", + "quantize.startFailed": "작업을 시작하지 못했습니다", + "quantize.stopFailed": "작업을 중지하지 못했습니다", + "quantize.deleteFailed": "작업을 삭제하지 못했습니다", + "quantize.status.queued": "대기 중", + "quantize.status.downloading": "다운로드 중", + "quantize.status.converting": "변환 중", + "quantize.status.quantizing": "양자화 중", + "quantize.status.completed": "완료됨", + "quantize.status.failed": "실패", + "quantize.status.stopped": "중지됨", + "off.badge": "사용할 수 없음", + "off.fineTune.title": "이 계정은 파인튜닝을 할 수 없습니다", + "off.fineTune.text": "파인튜닝은 별도의 권한이며, 이 계정에는 그 권한이 없습니다.", + "off.quantize.title": "이 계정은 양자화를 할 수 없습니다", + "off.quantize.text": "양자화는 별도의 권한이며, 이 계정에는 그 권한이 없습니다.", + "off.admin": "관리자가 사용자 및 키 메뉴에서 켤 수 있습니다.", + "off.askAdmin": "관리자에게 이 계정에 대해 켜 달라고 요청하세요.", + "off.back": "홈으로 이동", + "run.unknownPercent": "진행률이 아직 보고되지 않았습니다" +} diff --git a/addons/quantization/ui/src/i18n/quantization.pt-BR.json b/addons/quantization/ui/src/i18n/quantization.pt-BR.json new file mode 100644 index 0000000000..ee9754958d --- /dev/null +++ b/addons/quantization/ui/src/i18n/quantization.pt-BR.json @@ -0,0 +1,180 @@ +{ + "steps.label": "Progresso", + "steps.setup": "Configurar", + "steps.check": "Verificar", + "steps.run": "Executar", + "steps.result": "Resultado", + "run.stop": "Parar", + "run.connecting": "Conectando ao job. Aguardando o primeiro evento de progresso.", + "run.progress": "Progresso", + "run.stages": "Etapas", + "run.step": "passo", + "run.of": "de", + "run.epoch": "época", + "run.tokens": "tokens/s", + "run.eta": "cerca de {{time}} restantes", + "run.etaSeconds": "cerca de {{seconds}} s restantes", + "landing.experimental": "Experimental", + "checks.heading": "Verifique antes de começar", + "checks.live": "Atualiza conforme você muda a configuração.", + "checks.meterLabel": "{{used}} por cento em uso, {{free}} livres de {{total}}", + "checks.openBackends": "Abrir Backends", + "checks.barOk": "Nada impede o início.", + "checks.barWarn_one": "{{count}} aviso. Você ainda pode iniciar.", + "checks.barWarn_other": "{{count}} avisos. Você ainda pode iniciar.", + "checks.title.model": "Modelo", + "checks.title.data": "Dataset", + "checks.title.backend": "Backend", + "checks.title.memory": "Memória", + "checks.title.disk": "Disco", + "checks.title.reward": "Funções de recompensa", + "checks.title.resume": "Retomando", + "checks.title.access": "Acesso ao modelo", + "checks.title.type": "Tipo de quantização", + "checks.title.source": "Origem", + "checks.title.name": "Nome", + "checks.fineTune.modelOk": "Treinando {{model}}.", + "checks.fineTune.modelMissing": "Informe o modelo a treinar.", + "checks.fineTune.dataOk": "Os dados vêm de {{source}}.", + "checks.fineTune.dataMissing": "Adicione um dataset: um id do Hugging Face ou um arquivo enviado.", + "checks.fineTune.backendOk": "{{name}} está instalado.", + "checks.fineTune.backendMissing": "Nenhum backend de ajuste fino está instalado, então o job pode não iniciar.", + "checks.fineTune.backendOther": "{{name}} não está entre os backends de ajuste fino instalados ({{list}}).", + "checks.fineTune.backendUnknown": "Não foi possível ler os backends instalados.", + "checks.fineTune.memoryGpu": "{{name}}: {{free}} de {{total}} livres. Tamanho do lote {{batch}}, comprimento da sequência {{seq}}, acumulação de gradiente {{accum}}. Quanto o job precisa depende do modelo e destas configurações, e o LocalAI não estima isso antes de o job começar.", + "checks.fineTune.memoryCluster": "{{total}} no melhor nó ({{node}}). Tamanho do lote {{batch}}, comprimento da sequência {{seq}}. Quanto o job precisa depende do modelo e destas configurações.", + "checks.fineTune.memoryCpu": "Nenhuma GPU encontrada. O treino usa a CPU e {{free}} de {{total}} de RAM estão livres. Espere que seja lento. Tamanho do lote {{batch}}, comprimento da sequência {{seq}}.", + "checks.fineTune.memoryUnknown": "Esta conta não consegue ler a memória da máquina. Tamanho do lote {{batch}}, comprimento da sequência {{seq}}. Quanto o job precisa depende do modelo e destas configurações.", + "checks.fineTune.disk": "{{free}} livres no disco de modelos. Downloads e checkpoints precisam de espaço.", + "checks.fineTune.diskLow": "Só {{free}} livres no disco de modelos. Downloads e checkpoints precisam de espaço.", + "checks.fineTune.rewardMissing": "GRPO precisa de pelo menos uma função de recompensa. Adicione uma em Mais opções.", + "checks.fineTune.resume": "O treino continua a partir de {{path}}.", + "checks.fineTune.accessNone": "Nenhum token definido. Um modelo com acesso restrito precisa de um token do Hugging Face em Mais opções.", + "checks.fineTune.accessSet": "Um token do Hugging Face está definido.", + "checks.quantize.modelOk": "Quantizando {{model}}.", + "checks.quantize.modelMissing": "Informe o modelo a quantizar.", + "checks.quantize.typeMissing": "Informe o tipo de quantização personalizado.", + "checks.quantize.backendOk": "{{name}} está instalado.", + "checks.quantize.backendMissing": "Nenhum backend de quantização está instalado, então o job pode não iniciar.", + "checks.quantize.backendOther": "{{name}} não está entre os backends de quantização instalados ({{list}}).", + "checks.quantize.backendUnknown": "Não foi possível ler os backends instalados.", + "checks.quantize.memory": "{{free}} de {{total}} de RAM livres. A conversão roda na CPU.", + "checks.quantize.disk": "{{free}} livres no disco de modelos. O download, o arquivo convertido e o resultado são gravados em disco.", + "checks.quantize.diskLow": "Só {{free}} livres no disco de modelos. O download, o arquivo convertido e o resultado são gravados em disco.", + "checks.quantize.accessNone": "Nenhum token definido. Um modelo com acesso restrito precisa de um token do Hugging Face em Mais opções.", + "checks.quantize.accessSet": "Um token do Hugging Face está definido.", + "checks.import.sourceOk_huggingface": "Reconhecido como um repositório do Hugging Face. O LocalAI verifica os arquivos dele ao importar.", + "checks.import.sourceOk_url": "Formato de URL reconhecido. O conteúdo não foi verificado.", + "checks.import.sourceOk_config": "Reconhecido como um arquivo de configuração de modelo. Ele é lido e usado como está.", + "checks.import.sourceOk_oci": "Reconhecido como uma referência de imagem OCI.", + "checks.import.sourceOk_ollama": "Reconhecido como uma referência de modelo do Ollama.", + "checks.import.sourceOk_local": "Reconhecido como um caminho na máquina que executa o LocalAI.", + "checks.import.sourceOk": "A origem foi reconhecida.", + "checks.import.sourceUnknown": "Isto não parece com nenhum formato que o LocalAI conheça. Ele vai tentar mesmo assim, e pode falhar.", + "checks.import.backendDownload": "{{name}} ainda não está instalado. O LocalAI o baixa primeiro.", + "checks.import.backendChosen": "{{name}} foi escolhido e está instalado.", + "checks.import.backendAuto": "O LocalAI escolhe o backend pelos arquivos da origem quando a importação começa.", + "checks.import.nameTaken": "Já existe um modelo instalado chamado {{name}}.", + "checks.import.nameFree": "{{name}} não está em uso.", + "checks.import.nameAuto": "O modelo recebe o nome da origem.", + "checks.import.disk": "{{free}} livres no disco de modelos. O tamanho do download é conhecido quando a importação começa.", + "checks.import.memory_gpu": "{{free}} de memória da GPU estão livres agora. O que este modelo precisa é conhecido quando a importação começa.", + "checks.import.memory_ram": "{{free}} de RAM estão livres agora. O que este modelo precisa é conhecido quando a importação começa.", + "checks.import.memory_cluster": "{{free}} estão disponíveis no melhor nó. O que este modelo precisa é conhecido quando a importação começa.", + "checks.import.memory": "{{free}} de memória estão livres agora. O que este modelo precisa é conhecido quando a importação começa.", + "form.more": "Mais opções", + "form.backend": "Backend", + "form.token": "Token do Hugging Face", + "fineTune.starting": "Iniciando...", + "fineTune.noMessage": "O backend não enviou mensagem.", + "fineTune.tryTitle": "O que fazer a seguir", + "fineTune.reuse": "Abrir a configuração de novo", + "stop.cancelDelete": "Cancelar", + "log.title": "Log", + "log.note": "O que o job informou desde que esta página foi aberta.", + "log.only": "Somente avisos e erros", + "log.copy": "Copiar", + "log.copied": "Copiado", + "log.download": "Baixar", + "log.empty": "Nada informado ainda.", + "log.noneFiltered": "Nenhum aviso ou erro.", + "jobs.title": "Jobs anteriores", + "jobs.count_one": "{{count}} mantido neste servidor", + "jobs.count_other": "{{count}} mantidos neste servidor", + "jobs.loading": "Carregando jobs", + "jobs.emptyTitle": "Nenhum job de ajuste fino ainda", + "jobs.emptyText": "Configure um acima. Ele aparece aqui com o seu status, e você pode reutilizar a configuração depois.", + "jobs.model": "Modelo", + "jobs.kind": "Tipo", + "jobs.status": "Status", + "jobs.started": "Iniciado", + "jobs.actions": "Ações", + "jobs.open": "Abrir este job", + "jobs.reuse": "Reutilizar", + "jobs.reuseTitle": "Usar a configuração deste job em um novo job", + "jobs.delete": "Excluir", + "jobs.deleteTitle": "Excluir este job e os seus dados", + "jobs.deleteDialogTitle": "Excluir este job?", + "jobs.deleteDialogText": "Isto exclui o job com os seus checkpoints e qualquer modelo exportado. Não pode ser desfeito.", + "quantize.title": "Quantizar", + "quantize.lede": "Reduza um modelo para que ele caiba em menos memória. O resultado é um arquivo GGUF que você pode importar.", + "quantize.newJob": "Novo job", + "quantize.sectionModel": "Modelo", + "quantize.model": "Modelo", + "quantize.modelHint": "Um id do Hugging Face ou um caminho nesta máquina.", + "quantize.sectionPrecision": "Precisão", + "quantize.type": "Tipo de quantização", + "quantize.typeHint": "Um número menor significa arquivo menor e um pouco menos de qualidade.", + "quantize.custom": "Personalizado...", + "quantize.customLabel": "Tipo personalizado", + "quantize.customPlaceholder": "por exemplo iq4_nl", + "quantize.moreHint": "backend, token do Hugging Face", + "quantize.tokenHint": "Só é necessário para modelos com acesso restrito.", + "quantize.recipe": "Converter {{model}} para GGUF em {{type}}.", + "quantize.recipeEmpty": "Informe um modelo para ver o que será criado.", + "quantize.start": "Quantizar ({{type}})", + "quantize.barNote": "Nada é alterado até você iniciar.", + "quantize.jobMeta": "{{type}} em {{backend}}, iniciado {{started}}", + "quantize.failed": "A quantização falhou", + "quantize.finished": "Quantização concluída: {{type}}.", + "quantize.stopped": "O job foi interrompido.", + "quantize.tryText": "Abra a configuração de novo, altere o que a mensagem aponta e inicie um novo job. Um backend mais novo pode suportar uma arquitetura que um mais antigo não suporta.", + "quantize.outputTitle": "O resultado", + "quantize.outputText": "Um arquivo GGUF em {{type}}. Importe-o para usar no LocalAI, ou baixe-o.", + "quantize.file": "Arquivo", + "quantize.importName": "Nome do modelo", + "quantize.importNamePlaceholder": "Gerado se vazio", + "quantize.importBlocked": "A importação está desativada neste servidor porque ele não compartilha a pasta de modelos com o host do complemento.", + "quantize.import": "Importar para o LocalAI", + "quantize.importing": "Importando...", + "quantize.importFailed": "A importação falhou", + "quantize.importFailedWith": "A importação falhou: {{message}}", + "quantize.download": "Baixar GGUF", + "quantize.nextTitle": "{{name}} agora é um modelo", + "quantize.nextText": "Ele está registrado no LocalAI. Abra um chat com ele ou encontre-o em Modelos.", + "quantize.chat": "Conversar com {{name}}", + "quantize.models": "Abrir em Modelos", + "quantize.imported": "importado como {{name}}", + "quantize.emptyTitle": "Nenhum job de quantização ainda", + "quantize.emptyText": "Configure um acima. Ele aparece aqui com o seu status.", + "quantize.deleteDialogText": "Isto exclui o job com os seus arquivos convertidos. Não pode ser desfeito.", + "quantize.startFailed": "Não foi possível iniciar o job", + "quantize.stopFailed": "Não foi possível parar o job", + "quantize.deleteFailed": "Não foi possível excluir o job", + "quantize.status.queued": "Na fila", + "quantize.status.downloading": "Baixando", + "quantize.status.converting": "Convertendo", + "quantize.status.quantizing": "Quantizando", + "quantize.status.completed": "Concluído", + "quantize.status.failed": "Falhou", + "quantize.status.stopped": "Parado", + "off.badge": "Não disponível para você", + "off.fineTune.title": "Sua conta não pode fazer ajuste fino", + "off.fineTune.text": "O ajuste fino é uma permissão separada, e esta conta não a tem.", + "off.quantize.title": "Sua conta não pode quantizar", + "off.quantize.text": "A quantização é uma permissão separada, e esta conta não a tem.", + "off.admin": "Um administrador pode ativá-la em Usuários e chaves.", + "off.askAdmin": "Peça a um administrador para ativá-la na sua conta.", + "off.back": "Ir para o Início", + "run.unknownPercent": "Progresso ainda não informado" +} diff --git a/addons/quantization/ui/src/i18n/quantization.zh-CN.json b/addons/quantization/ui/src/i18n/quantization.zh-CN.json new file mode 100644 index 0000000000..1b2fe3d3a4 --- /dev/null +++ b/addons/quantization/ui/src/i18n/quantization.zh-CN.json @@ -0,0 +1,180 @@ +{ + "steps.label": "进度", + "steps.setup": "设置", + "steps.check": "检查", + "steps.run": "运行", + "steps.result": "结果", + "run.stop": "停止", + "run.connecting": "正在连接任务。等待第一个进度事件。", + "run.progress": "进度", + "run.stages": "阶段", + "run.step": "步", + "run.of": "/", + "run.epoch": "轮次", + "run.tokens": "tokens/s", + "run.eta": "约剩 {{time}}", + "run.etaSeconds": "约剩 {{seconds}} 秒", + "landing.experimental": "实验性", + "checks.heading": "开始前检查", + "checks.live": "随您修改设置而更新。", + "checks.meterLabel": "已用 {{used}}%,{{total}} 中 {{free}} 可用", + "checks.openBackends": "打开后端", + "checks.barOk": "没有任何项目阻止启动。", + "checks.barWarn_one": "{{count}} 条警告。仍可以启动。", + "checks.barWarn_other": "{{count}} 条警告。仍可以启动。", + "checks.title.model": "模型", + "checks.title.data": "数据集", + "checks.title.backend": "后端", + "checks.title.memory": "内存", + "checks.title.disk": "磁盘", + "checks.title.reward": "奖励函数", + "checks.title.resume": "恢复", + "checks.title.access": "模型访问", + "checks.title.type": "量化类型", + "checks.title.source": "来源", + "checks.title.name": "名称", + "checks.fineTune.modelOk": "正在训练 {{model}}。", + "checks.fineTune.modelMissing": "请输入要训练的模型。", + "checks.fineTune.dataOk": "数据来自 {{source}}。", + "checks.fineTune.dataMissing": "请添加数据集:Hugging Face id 或上传的文件。", + "checks.fineTune.backendOk": "{{name}} 已安装。", + "checks.fineTune.backendMissing": "未安装微调后端,任务可能无法启动。", + "checks.fineTune.backendOther": "{{name}} 不在已安装的微调后端({{list}})中。", + "checks.fineTune.backendUnknown": "无法读取已安装的后端。", + "checks.fineTune.memoryGpu": "{{name}}:{{total}} 中 {{free}} 可用。批次大小 {{batch}},序列长度 {{seq}},梯度累积 {{accum}}。任务所需的内存取决于模型和这些设置,LocalAI 在任务开始前不做估算。", + "checks.fineTune.memoryCluster": "最佳节点({{node}})上的 {{total}}。批次大小 {{batch}},序列长度 {{seq}}。任务所需的内存取决于模型和这些设置。", + "checks.fineTune.memoryCpu": "未找到 GPU。训练使用 CPU,RAM {{total}} 中有 {{free}} 可用。预计会很慢。批次大小 {{batch}},序列长度 {{seq}}。", + "checks.fineTune.memoryUnknown": "此账户无法读取机器的内存。批次大小 {{batch}},序列长度 {{seq}}。任务所需的内存取决于模型和这些设置。", + "checks.fineTune.disk": "模型磁盘上可用 {{free}}。下载和检查点需要空间。", + "checks.fineTune.diskLow": "模型磁盘上仅剩 {{free}} 可用。下载和检查点需要空间。", + "checks.fineTune.rewardMissing": "GRPO 至少需要一个奖励函数。请在“更多选项”中添加。", + "checks.fineTune.resume": "训练将从 {{path}} 继续。", + "checks.fineTune.accessNone": "未设置令牌。受限模型需要在“更多选项”中设置 Hugging Face 令牌。", + "checks.fineTune.accessSet": "已设置 Hugging Face 令牌。", + "checks.quantize.modelOk": "正在量化 {{model}}。", + "checks.quantize.modelMissing": "请输入要量化的模型。", + "checks.quantize.typeMissing": "请输入自定义量化类型。", + "checks.quantize.backendOk": "{{name}} 已安装。", + "checks.quantize.backendMissing": "未安装量化后端,任务可能无法启动。", + "checks.quantize.backendOther": "{{name}} 不在已安装的量化后端({{list}})中。", + "checks.quantize.backendUnknown": "无法读取已安装的后端。", + "checks.quantize.memory": "RAM {{total}} 中有 {{free}} 可用。转换在 CPU 上运行。", + "checks.quantize.disk": "模型磁盘上可用 {{free}}。下载、转换后的文件和结果都会写入磁盘。", + "checks.quantize.diskLow": "模型磁盘上仅剩 {{free}} 可用。下载、转换后的文件和结果都会写入磁盘。", + "checks.quantize.accessNone": "未设置令牌。受限模型需要在“更多选项”中设置 Hugging Face 令牌。", + "checks.quantize.accessSet": "已设置 Hugging Face 令牌。", + "checks.import.sourceOk_huggingface": "识别为 Hugging Face 仓库。导入时 LocalAI 会检查其中的文件。", + "checks.import.sourceOk_url": "已识别 URL 格式。其内容尚未检查。", + "checks.import.sourceOk_config": "识别为模型配置文件。将按原样读取并使用。", + "checks.import.sourceOk_oci": "识别为 OCI 镜像引用。", + "checks.import.sourceOk_ollama": "识别为 Ollama 模型引用。", + "checks.import.sourceOk_local": "识别为运行 LocalAI 的机器上的路径。", + "checks.import.sourceOk": "已识别来源。", + "checks.import.sourceUnknown": "这看起来不是 LocalAI 支持的任何格式。仍会尝试,但可能失败。", + "checks.import.backendDownload": "{{name}} 尚未安装。LocalAI 会先下载它。", + "checks.import.backendChosen": "已选择并安装 {{name}}。", + "checks.import.backendAuto": "导入开始时,LocalAI 会根据来源的文件选择后端。", + "checks.import.nameTaken": "名为 {{name}} 的模型已安装。", + "checks.import.nameFree": "{{name}} 未被使用。", + "checks.import.nameAuto": "模型将以来源命名。", + "checks.import.disk": "模型磁盘上可用 {{free}}。导入开始后即可知道下载大小。", + "checks.import.memory_gpu": "当前 GPU 显存可用 {{free}}。此模型需要多少,导入开始后即可知道。", + "checks.import.memory_ram": "当前 RAM 可用 {{free}}。此模型需要多少,导入开始后即可知道。", + "checks.import.memory_cluster": "最佳节点上可用 {{free}}。此模型需要多少,导入开始后即可知道。", + "checks.import.memory": "当前内存可用 {{free}}。此模型需要多少,导入开始后即可知道。", + "form.more": "更多选项", + "form.backend": "后端", + "form.token": "Hugging Face 令牌", + "fineTune.starting": "正在启动...", + "fineTune.noMessage": "后端没有提供消息。", + "fineTune.tryTitle": "下一步该怎么做", + "fineTune.reuse": "重新打开设置", + "stop.cancelDelete": "取消", + "log.title": "日志", + "log.note": "自打开此页面以来任务报告的内容。", + "log.only": "仅显示警告和错误", + "log.copy": "复制", + "log.copied": "已复制", + "log.download": "下载", + "log.empty": "尚无报告。", + "log.noneFiltered": "没有警告或错误。", + "jobs.title": "早前的任务", + "jobs.count_one": "此服务器保留 {{count}} 个", + "jobs.count_other": "此服务器保留 {{count}} 个", + "jobs.loading": "正在加载任务", + "jobs.emptyTitle": "还没有微调任务", + "jobs.emptyText": "在上方进行设置。任务会连同状态显示在这里,之后您可以复用其设置。", + "jobs.model": "模型", + "jobs.kind": "类型", + "jobs.status": "状态", + "jobs.started": "开始时间", + "jobs.actions": "操作", + "jobs.open": "打开此任务", + "jobs.reuse": "复用", + "jobs.reuseTitle": "将此任务的设置用于新任务", + "jobs.delete": "删除", + "jobs.deleteTitle": "删除此任务及其数据", + "jobs.deleteDialogTitle": "删除此任务?", + "jobs.deleteDialogText": "这将删除任务及其检查点和任何已导出的模型。此操作无法撤销。", + "quantize.title": "量化", + "quantize.lede": "缩小模型,使其占用更少内存。结果是可以导入的 GGUF 文件。", + "quantize.newJob": "新建任务", + "quantize.sectionModel": "模型", + "quantize.model": "模型", + "quantize.modelHint": "Hugging Face id,或本机上的路径。", + "quantize.sectionPrecision": "精度", + "quantize.type": "量化类型", + "quantize.typeHint": "数字越小,文件越小,质量会略有下降。", + "quantize.custom": "自定义...", + "quantize.customLabel": "自定义类型", + "quantize.customPlaceholder": "例如 iq4_nl", + "quantize.moreHint": "后端、Hugging Face 令牌", + "quantize.tokenHint": "仅受限模型需要。", + "quantize.recipe": "将 {{model}} 转换为 {{type}} 格式的 GGUF。", + "quantize.recipeEmpty": "输入模型,即可查看将要生成的内容。", + "quantize.start": "量化({{type}})", + "quantize.barNote": "在您开始之前,不会更改任何内容。", + "quantize.jobMeta": "{{backend}} 上的 {{type}},开始于 {{started}}", + "quantize.failed": "量化失败", + "quantize.finished": "量化完成:{{type}}。", + "quantize.stopped": "任务已停止。", + "quantize.tryText": "重新打开设置,修改消息所指的内容,然后启动新任务。较新的后端可能支持较旧后端不支持的架构。", + "quantize.outputTitle": "结果", + "quantize.outputText": "{{type}} 格式的 GGUF 文件。导入后即可在 LocalAI 中使用,也可以直接下载。", + "quantize.file": "文件", + "quantize.importName": "模型名称", + "quantize.importNamePlaceholder": "留空则自动生成", + "quantize.importBlocked": "此服务器未与附加组件主机共享模型文件夹,因此已关闭导入。", + "quantize.import": "导入到 LocalAI", + "quantize.importing": "正在导入...", + "quantize.importFailed": "导入失败", + "quantize.importFailedWith": "导入失败:{{message}}", + "quantize.download": "下载 GGUF", + "quantize.nextTitle": "{{name}} 现已成为模型", + "quantize.nextText": "它已在 LocalAI 中注册。打开与它的对话,或在模型页面中找到它。", + "quantize.chat": "与 {{name}} 对话", + "quantize.models": "在模型中打开", + "quantize.imported": "已导入为 {{name}}", + "quantize.emptyTitle": "还没有量化任务", + "quantize.emptyText": "在上方进行设置。任务会连同状态显示在这里。", + "quantize.deleteDialogText": "这将删除任务及其转换后的文件。此操作无法撤销。", + "quantize.startFailed": "启动任务失败", + "quantize.stopFailed": "停止任务失败", + "quantize.deleteFailed": "删除任务失败", + "quantize.status.queued": "排队中", + "quantize.status.downloading": "下载中", + "quantize.status.converting": "转换中", + "quantize.status.quantizing": "量化中", + "quantize.status.completed": "已完成", + "quantize.status.failed": "失败", + "quantize.status.stopped": "已停止", + "off.badge": "您无法使用", + "off.fineTune.title": "您的账户无法进行微调", + "off.fineTune.text": "微调是一项单独的权限,此账户没有该权限。", + "off.quantize.title": "您的账户无法进行量化", + "off.quantize.text": "量化是一项单独的权限,此账户没有该权限。", + "off.admin": "管理员可以在“用户和密钥”中开启该权限。", + "off.askAdmin": "请管理员为您的账户开启该权限。", + "off.back": "前往首页", + "run.unknownPercent": "尚未报告进度" +} diff --git a/addons/quantization/ui/test/page.test.mjs b/addons/quantization/ui/test/page.test.mjs index 3f4031efa9..02559062a5 100644 --- a/addons/quantization/ui/test/page.test.mjs +++ b/addons/quantization/ui/test/page.test.mjs @@ -5,9 +5,9 @@ import { join } from 'node:path' import { pathToFileURL } from 'node:url' import { load, draw, here, hostUi, React, renderToString } from './load.mjs' import { createApi } from '../src/api.js' -import en from '../src/i18n/en.js' const read = (p) => readFileSync(p, 'utf8') +const en = JSON.parse(read(join(here, '../src/i18n/quantization.en.json'))) const src = join(here, '../src') const toolsUi = join(here, '../../../internal/tools-ui/src') const files = (dir) => readdirSync(dir, { recursive: true }).filter(f => /\.(jsx?|mjs)$/.test(f)).map(f => join(dir, f)) @@ -180,7 +180,7 @@ test('icons: every icon name the page draws is one the SDK knows', async () => { for (const name of used) assert.ok(known.has(name), `unknown icon ${name}`) }) -test('i18n: every literal key the add-on and the shared views read is in en.js', () => { +test('i18n: every literal key the add-on and the shared views read is in the English bundle', () => { const keys = new Set() for (const text of [ownText, sharedText]) for (const m of text.matchAll(/\bt\(\s*'([A-Za-z0-9_.]+)'/g)) keys.add(m[1]) assert.ok(keys.size > 60, `found only ${keys.size} literal keys`) @@ -188,7 +188,7 @@ test('i18n: every literal key the add-on and the shared views read is in en.js', assert.deepEqual(missing, []) }) -test('i18n: en.js repeats the host strings and adds only the keys the add-on owns', () => { +test('i18n: the English bundle repeats the host strings and adds only the keys the add-on owns', () => { const tools = Object.fromEntries(flatten(JSON.parse(read(join(hostUi, 'public/locales/en/tools.json'))))) const own = [] for (const [k, v] of Object.entries(en)) { diff --git a/addons/voice/ui/src/ctx.jsx b/addons/voice/ui/src/ctx.jsx index 324b6107bd..e8696b295d 100644 --- a/addons/voice/ui/src/ctx.jsx +++ b/addons/voice/ui/src/ctx.jsx @@ -1,4 +1,4 @@ -import en from './i18n/en.js' +import { locales } from './i18n/index.js' import { modelFrom as pickModel } from './lib/model.js' // The one object every page factory of the Voice add-on receives. It binds the @@ -8,12 +8,25 @@ export function createContext(sdk) { const { React } = sdk const h = React.createElement const Fragment = React.Fragment - sdk.locale.define('en', Object.fromEntries(Object.entries(en).filter(([, v]) => typeof v === 'string'))) - const lists = Object.fromEntries(Object.entries(en).filter(([, v]) => Array.isArray(v))) + // Every language of the page; a key a language lacks reads in English. + for (const [language, bundle] of Object.entries(locales)) sdk.locale.define(language, bundle) + const en = locales.en const t = (key, vars) => sdk.locale.t(key, key, vars) - // A list of strings or objects (the "why" and "stored" notes). - const list = (key) => lists[key] || [] + // A list of strings or objects (the "why" and "stored" notes). The bundles hold + // each item under a numbered key (key.0, or key.0.field for an object), so + // that every item is translated like any other text. + const list = (key) => { + const out = [] + for (let i = 0; ; i++) { + const own = `${key}.${i}` + if (typeof en[own] === 'string') { out.push(t(own)); continue } + const prefix = `${own}.` + const fields = Object.keys(en).filter((k) => k.startsWith(prefix)) + if (fields.length === 0) return out + out.push(Object.fromEntries(fields.map((k) => [k.slice(prefix.length), t(k)]))) + } + } // A link to a page of this add-on, as an anchor the shell navigates. function Link({ page, query, className, children, ...rest }) { diff --git a/addons/voice/ui/src/i18n/index.js b/addons/voice/ui/src/i18n/index.js new file mode 100644 index 0000000000..2a0ea9c09d --- /dev/null +++ b/addons/voice/ui/src/i18n/index.js @@ -0,0 +1,16 @@ +// The words of the voice pages, one JSON bundle per language of the web UI. +// English is the source and the fallback of every key. Italian is a draft +// that wants a native review; a language whose bundle repeats the English +// text of a key has no translation for it yet. addons/internal/i18ncheck +// checks that every bundle has the keys and placeholders of English. +import en from './voice.en.json' +import it from './voice.it.json' +import de from './voice.de.json' +import es from './voice.es.json' +import fr from './voice.fr.json' +import id from './voice.id.json' +import ko from './voice.ko.json' +import pt_BR from './voice.pt-BR.json' +import zh_CN from './voice.zh-CN.json' + +export const locales = { en, it, de, es, fr, id, ko, 'pt-BR': pt_BR, 'zh-CN': zh_CN } diff --git a/addons/voice/ui/src/i18n/voice.de.json b/addons/voice/ui/src/i18n/voice.de.json new file mode 100644 index 0000000000..432c0d2b6c --- /dev/null +++ b/addons/voice/ui/src/i18n/voice.de.json @@ -0,0 +1,341 @@ +{ + "voice.adminOnly.title": "Administrators only", + "voice.adminOnly.text": "Saved voices can be managed by an administrator of this server. Ask one if you need a voice added or removed.", + "speech.title": "Speak text", + "speech.lede": "Turn text into speech with a voice model of this server.", + "speech.model": "Model", + "speech.voice": "Voice", + "speech.defaultVoice": "Default voice", + "speech.text": "Text", + "speech.instructions": "Instructions (optional)", + "speech.generate": "Generate speech", + "speech.result": "Ergebnis", + "speech.noModels": "No text-to-speech model is installed.", + "speech.noModelsLink": "Get one from the model gallery.", + "speech.voicePlaceholder": "Optionale Stimm-ID", + "speech.textPlaceholder": "What should it say?", + "speech.noAccess": "Your account has no access to text-to-speech. The server will refuse the request.", + "voices.title": "Stimmen", + "voices.lede": "Recognise who is speaking, and keep voices that text-to-speech can use.", + "voices.tabs": "Voices sections", + "voices.tab.recognition": "Erkennung", + "voices.tab.library": "Bibliothek", + "voices.tab.speech": "Text to speech", + "voices.tab.recording": "From a recording", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "In Arbeit", + "voice.speaker": "speaker", + "voice.print": "voiceprint", + "voice.whoTab": "Who is this", + "voice.whoLede": "Matches a clip against the people you enrolled.", + "voice.sameLede": "Two clips, one question. Nobody has to be enrolled.", + "voice.whoGo": "Identify speaker", + "voice.working": "Identifying", + "voice.whoNote": "Searches the speakers enrolled on this server. The clip you test with is not stored.", + "voice.sameNote": "The answer comes from one voiceprint per clip. Nothing is stored.", + "voice.workingText": "Comparing the clip with the voiceprints the server holds.", + "voice.probably": "Probably {{name}}", + "voice.matchText": "{{name}}'s voiceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "voice.nobody": "Nobody enrolled sounds like this", + "voice.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new speaker, or a clip that is too short or noisy.", + "voice.noneEnrolled": "Nobody is enrolled to compare with", + "voice.noneEnrolledText": "Enrol a speaker first, then identify a clip against them.", + "voice.noneReturned": "The server returned no speakers", + "voice.noneReturnedText": "It holds no voiceprints made with this model. If the server restarted, enrolled voices are gone: enrol them again.", + "voice.enrolThis": "Enrol this voice", + "voice.same": "Probably the same person", + "voice.different": "Probably different people", + "voice.knownTitle": "Known speakers", + "voice.enrol": "Enrol a speaker", + "voice.enrolTitle": "Enrol a speaker", + "voice.enrolIntro": "The server keeps a voiceprint and a name. It does not keep the recording.", + "voice.emptyTitle": "Nobody enrolled in this browser yet", + "voice.emptyText": "Enrol a speaker with a short recording, then test a clip against them.", + "voice.agreedHint": "I have their permission, or another lawful basis, to store a voiceprint of them.", + "voice.keepHint": "Lets you replay it and enrol again after a server restart. Off by default. It is stored in this browser only.", + "voice.neededTitle": "Voices need a speaker model", + "voice.neededText": "Voice recognition turns a recording into a voiceprint and compares voiceprints. It runs on a speaker-recognition model, and none is installed on this server.", + "voice.neededWhat": "Install a model that supports speaker recognition. Once one is installed, this page turns on by itself. Users also need the Voice recognition permission.", + "voice.moreTitle": "More tools", + "voice.moreHint": "guess a speaker's traits, get the raw voiceprint", + "voice.why.0": "One speaker is assumed. A clip with two people mixes both voices.", + "voice.why.1": "A different microphone or room changes how a voice sounds.", + "voice.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "voice.stored.0.where": "On the server", + "voice.stored.0.what": "A {{print}} (numbers that describe the voice, not the audio), a name and your labels. They are kept in memory, are shared by everyone who uses this server, and are lost when it restarts. The recording is not kept.", + "voice.stored.1.where": "In this browser", + "voice.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the recording only if you tick Keep a copy when enrolling.", + "voice.stored.2.where": "Clips you test with", + "voice.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "Name", + "known.added": "Hinzugefügt", + "known.actions": "Aktionen", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "Rückgängig", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "{{name}} entfernen", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "enrol.close": "Schließen", + "enrol.cancel": "Abbrechen", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Recording", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Recording", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "Name", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Exact transcript", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "tts.title": "Text zu Sprache", + "tts.labels.model": "Modell", + "tts.labels.voice": "Stimme", + "tts.labels.voicePlaceholder": "Optionale Stimm-ID", + "tts.labels.input": "Text", + "tts.labels.inputPlaceholder": "Text zum Synthetisieren eingeben...", + "tts.labels.instructions": "Anweisungen", + "tts.labels.instructionsPlaceholder": "Zum Beispiel: Langsam und warmherzig sprechen", + "tts.labels.instructionsHint": "Optionale Hinweise zur Sprechweise. Die Unterstützung hängt vom Backend ab.", + "tts.actions.generate": "Audio generieren", + "tts.actions.generating": "Generieren...", + "tts.empty": "Generiertes Audio erscheint hier", + "tts.toasts.noText": "Bitte Text eingeben", + "tts.toasts.noModel": "Bitte ein Modell auswählen", + "tts.toasts.generated": "Speech generated", + "tts.toasts.generateFailed": "Generierung fehlgeschlagen", + "tts.voiceLibrary.cloningReady": "Voice cloning", + "tts.voiceLibrary.loading": "Loading saved personalities…", + "tts.voiceLibrary.modelDefault": "Use model default", + "tts.voiceLibrary.empty": "No saved personalities yet.", + "tts.voiceLibrary.create": "Create one", + "tts.voiceLibrary.manage": "Manage Personality Library", + "tts.voiceLibrary.namedVoiceHint": "This model does not accept reference-audio profiles. You can still enter a named speaker supported by its backend.", + "voiceLibrary.title": "Speech voices", + "voiceLibrary.subtitle": "A recording and its exact transcript. Text-to-speech models that accept reference audio can speak with it.", + "voiceLibrary.loadFailed": "Speech voices could not be loaded", + "voiceLibrary.loading": "Loading saved personalities…", + "voiceLibrary.listLabel": "Saved voice personalities", + "voiceLibrary.status.ready": "Bereit", + "voiceLibrary.summary.label": "Voice library status", + "voiceLibrary.summary.profiles": "saved personalities", + "voiceLibrary.summary.modelsReady_one": "{{count}} compatible model ready", + "voiceLibrary.summary.modelsReady_other": "{{count}} compatible models ready", + "voiceLibrary.summary.noModels": "No compatible model installed", + "voiceLibrary.search.label": "Search voice profiles", + "voiceLibrary.search.placeholder": "Search voices, languages, or transcripts", + "voiceLibrary.filters.language": "Filter by language", + "voiceLibrary.filters.allLanguages": "All languages", + "voiceLibrary.empty.title": "No speech voices yet", + "voiceLibrary.empty.body": "Record or upload a consented reference clip, add its exact transcript, and reuse the voice across compatible text-to-speech models.", + "voiceLibrary.noResults.title": "No voices match these filters", + "voiceLibrary.noResults.body": "Try a different search or show every language.", + "voiceLibrary.detail.eyebrow": "Reference recording and exact transcript", + "voiceLibrary.detail.close": "Schließen", + "voiceLibrary.detail.emptyTitle": "Select a voice to inspect it", + "voiceLibrary.detail.emptyBody": "Reference audio, transcript, compatibility, and consent details will appear here.", + "voiceLibrary.detail.referenceAudio": "Reference audio", + "voiceLibrary.detail.transcript": "Reference transcript", + "voiceLibrary.metadata.language": "Sprache", + "voiceLibrary.metadata.languageUnknown": "Not specified", + "voiceLibrary.metadata.duration": "Duration", + "voiceLibrary.metadata.sampleRate": "Sample rate", + "voiceLibrary.metadata.created": "Erstellt", + "voiceLibrary.consent.label": "Permission", + "voiceLibrary.consent.confirmed": "Consent confirmed", + "voiceLibrary.consent.confirmedAt": "Confirmed {{date}}", + "voiceLibrary.modelSetup.title": "Install a voice-cloning model", + "voiceLibrary.modelSetup.body": "This voice is ready, but it needs a model that accepts reference audio. Choose one verified by this server.", + "voiceLibrary.modelSetup.loading": "Finding compatible models in your galleries…", + "voiceLibrary.modelSetup.backendUnknown": "Backend selected during install", + "voiceLibrary.modelSetup.install": "Installieren", + "voiceLibrary.modelSetup.installing": "Wird installiert…", + "voiceLibrary.modelSetup.loadFailed": "Compatible recommendations could not be loaded.", + "voiceLibrary.modelSetup.noneAvailable": "No compatible uninstalled model is available in the configured galleries.", + "voiceLibrary.modelSetup.capabilityNote": "Recommendations come from model capability metadata, not a browser-side model list.", + "voiceLibrary.api.title": "API usage and compatible models", + "voiceLibrary.api.summary": "Use this voice outside the browser or check model support", + "voiceLibrary.api.body": "Send the profile’s stable voice URI with each speech request. LocalAI resolves the private reference audio and transcript at generation time; the settings of your models are not changed.", + "voiceLibrary.api.compatibleModels": "Compatible installed models", + "voiceLibrary.api.noInstalledModels": "None yet. Install one of the server-verified models above before sending this request.", + "voiceLibrary.api.curlExample": "cURL example", + "voiceLibrary.api.copy": "Copy request", + "voiceLibrary.api.endpointNote": "The same model, input, and voice fields also work with POST /tts.", + "voiceLibrary.actions.create": "Add a speech voice", + "voiceLibrary.actions.createFirst": "Add your first speech voice", + "voiceLibrary.actions.retry": "Erneut versuchen", + "voiceLibrary.actions.clearFilters": "Filter zurücksetzen", + "voiceLibrary.actions.useInTTS": "Use in Text to Speech", + "voiceLibrary.actions.delete": "Delete voice", + "voiceLibrary.actions.installModel": "Install a compatible model", + "voiceLibrary.actions.browseModels": "Browse all models", + "voiceLibrary.deleteDialog.title": "Delete this voice?", + "voiceLibrary.deleteDialog.message": "“{{name}}” and its reference audio will be permanently removed. Models set up with this voice are not changed.", + "voiceLibrary.deleteDialog.deleting": "Deleting…", + "voiceLibrary.toasts.deleted": "Deleted {{name}}", + "voiceLibrary.toasts.installStarted": "Installing {{name}}. Progress is available in Operations.", + "voiceLibrary.toasts.installFailed": "Could not start installation: {{message}}", + "voiceLibrary.toasts.apiCopied": "API request copied", + "voiceLibrary.toasts.copyFailed": "Could not copy the API request", + "voiceCreate.title": "Add a speech voice", + "voiceCreate.subtitle": "A speech voice is one or more clean reference clips and their exact transcripts. Text-to-speech models that accept reference audio can speak with it.", + "voiceCreate.references.add": "Add another reference", + "voiceCreate.references.additional": "Reference {{number}}", + "voiceCreate.references.remove": "Entfernen", + "voiceCreate.sections.reference.title": "Reference audio", + "voiceCreate.sections.reference.body": "Record in the browser or upload an existing clip. Speech should be clean, natural, and from one speaker.", + "voiceCreate.sections.details.title": "Identity and transcript", + "voiceCreate.sections.details.body": "Give admins a recognizable name and transcribe the clip exactly, including fillers and punctuation.", + "voiceCreate.sections.consent.title": "Authorization", + "voiceCreate.sections.consent.body": "Voice cloning can be sensitive. Confirm permission before this reference is saved.", + "voiceCreate.audio.label": "Voice reference", + "voiceCreate.audio.normalizing": "Preparing a secure PCM WAV…", + "voiceCreate.audio.preview": "Normalized reference", + "voiceCreate.audio.tooLarge": "This file is larger than 50 MiB.", + "voiceCreate.audio.durationError": "Reference audio must be between 1 second and 2 minutes.", + "voiceCreate.audio.decodeError": "This browser could not decode the selected audio file.", + "voiceCreate.audio.qualityReady": "Recommended length", + "voiceCreate.audio.qualityHint": "6–30 seconds usually produces a stronger clone", + "voiceCreate.fields.name": "Voice name", + "voiceCreate.fields.namePlaceholder": "e.g. Documentary narrator", + "voiceCreate.fields.language": "Language (optional)", + "voiceCreate.fields.languagePlaceholder": "e.g. en-US", + "voiceCreate.fields.description": "Description (optional)", + "voiceCreate.fields.descriptionPlaceholder": "Tone, source, or intended use", + "voiceCreate.fields.transcript": "Exact transcript", + "voiceCreate.fields.transcriptPlaceholder": "Type every word spoken in the reference clip…", + "voiceCreate.fields.transcriptHint": "Match the recording exactly. Do not rewrite grammar or remove pauses and fillers.", + "voiceCreate.consent.title": "I confirm that this voice may be cloned", + "voiceCreate.consent.body": "I have the speaker’s permission or another lawful basis to store and use this recording for synthesized speech.", + "voiceCreate.readiness.title": "Profile readiness", + "voiceCreate.readiness.body": "A voice becomes available in Text to Speech when every required item is complete.", + "voiceCreate.readiness.audio": "Valid PCM-WAV reference", + "voiceCreate.readiness.quality": "6–30 second quality window", + "voiceCreate.readiness.name": "Recognizable voice name", + "voiceCreate.readiness.transcript": "Exact reference transcript", + "voiceCreate.readiness.consent": "Authorization confirmed", + "voiceCreate.privacy.title": "Private by default. ", + "voiceCreate.privacy.body": "Reference audio is stored in LocalAI’s data directory with restricted file permissions and is never exposed as a model-folder path.", + "voiceCreate.actions.back": "Back to library", + "voiceCreate.actions.cancel": "Abbrechen", + "voiceCreate.actions.save": "Save voice", + "voiceCreate.actions.saving": "Saving…", + "voiceCreate.toasts.created": "{{name}} is ready to use", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Record", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "Entfernen", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "Abbrechen", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "Stopp", + "clip.start": "Start recording", + "clip.recorded": "Recording", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "Vorschau", + "clip.photoAlt": "The photo you chose", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "stored.title": "What is stored, and where", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "Erneut versuchen", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "Installieren", + "needed.installing": "Wird installiert", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Browse all models", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "recording.title": "Name the speakers in a recording", + "recording.text": "The diarization workspace finds who spoke when in a recording. From there you can remember a speaker, and that voice joins the Speakers list here.", + "recording.step1": "Open the workspace, add a recording and tick Prepare speakers to remember.", + "recording.step2": "Run diarization. Each speaker with enough clean speech gets a Name and remember button.", + "recording.step3": "Name a speaker and remember it. That voice then appears under Speakers, ready for Who is this.", + "recording.open": "Open the diarization workspace", + "recording.off": "Diarization is not turned on for your account.", + "recording.warning": "Remembered voices are shared on this server and are lost when it restarts. Nothing is remembered automatically." +} diff --git a/addons/voice/ui/src/i18n/en.js b/addons/voice/ui/src/i18n/voice.en.json similarity index 95% rename from addons/voice/ui/src/i18n/en.js rename to addons/voice/ui/src/i18n/voice.en.json index 037e6fcd22..32a1583d41 100644 --- a/addons/voice/ui/src/i18n/en.js +++ b/addons/voice/ui/src/i18n/voice.en.json @@ -1,5 +1,4 @@ -// Generated once from the host locale files; edit by hand from here on. -export default { +{ "voice.adminOnly.title": "Administrators only", "voice.adminOnly.text": "Saved voices can be managed by an administrator of this server. Ask one if you need a voice added or removed.", "speech.title": "Speak text", @@ -68,25 +67,15 @@ export default { "voice.neededWhat": "Install a model that supports speaker recognition. Once one is installed, this page turns on by itself. Users also need the Voice recognition permission.", "voice.moreTitle": "More tools", "voice.moreHint": "guess a speaker's traits, get the raw voiceprint", - "voice.why": [ - "One speaker is assumed. A clip with two people mixes both voices.", - "A different microphone or room changes how a voice sounds.", - "This is a similarity score, not a security check. Do not use it to unlock anything." - ], - "voice.stored": [ - { - "where": "On the server", - "what": "A {{print}} (numbers that describe the voice, not the audio), a name and your labels. They are kept in memory, are shared by everyone who uses this server, and are lost when it restarts. The recording is not kept." - }, - { - "where": "In this browser", - "what": "The name, labels and time of each person you enrolled. A copy of the recording only if you tick Keep a copy when enrolling." - }, - { - "where": "Clips you test with", - "what": "They go to the model on this server to be compared and are not kept." - } - ], + "voice.why.0": "One speaker is assumed. A clip with two people mixes both voices.", + "voice.why.1": "A different microphone or room changes how a voice sounds.", + "voice.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "voice.stored.0.where": "On the server", + "voice.stored.0.what": "A {{print}} (numbers that describe the voice, not the audio), a name and your labels. They are kept in memory, are shared by everyone who uses this server, and are lost when it restarts. The recording is not kept.", + "voice.stored.1.where": "In this browser", + "voice.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the recording only if you tick Keep a copy when enrolling.", + "voice.stored.2.where": "Clips you test with", + "voice.stored.2.what": "They go to the model on this server to be compared and are not kept.", "cutoff.label": "Cut-off", "cutoff.default": "model default", "cutoff.reset": "Reset to {{value}}", diff --git a/addons/voice/ui/src/i18n/voice.es.json b/addons/voice/ui/src/i18n/voice.es.json new file mode 100644 index 0000000000..eb32ceda07 --- /dev/null +++ b/addons/voice/ui/src/i18n/voice.es.json @@ -0,0 +1,341 @@ +{ + "voice.adminOnly.title": "Administrators only", + "voice.adminOnly.text": "Saved voices can be managed by an administrator of this server. Ask one if you need a voice added or removed.", + "speech.title": "Speak text", + "speech.lede": "Turn text into speech with a voice model of this server.", + "speech.model": "Model", + "speech.voice": "Voice", + "speech.defaultVoice": "Default voice", + "speech.text": "Texto", + "speech.instructions": "Instructions (optional)", + "speech.generate": "Generate speech", + "speech.result": "Resultado", + "speech.noModels": "No text-to-speech model is installed.", + "speech.noModelsLink": "Get one from the model gallery.", + "speech.voicePlaceholder": "ID de voz opcional", + "speech.textPlaceholder": "What should it say?", + "speech.noAccess": "Your account has no access to text-to-speech. The server will refuse the request.", + "voices.title": "Voces", + "voices.lede": "Recognise who is speaking, and keep voices that text-to-speech can use.", + "voices.tabs": "Voices sections", + "voices.tab.recognition": "Reconocimiento", + "voices.tab.library": "Biblioteca", + "voices.tab.speech": "Text to speech", + "voices.tab.recording": "From a recording", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "En marcha", + "voice.speaker": "speaker", + "voice.print": "voiceprint", + "voice.whoTab": "Who is this", + "voice.whoLede": "Matches a clip against the people you enrolled.", + "voice.sameLede": "Two clips, one question. Nobody has to be enrolled.", + "voice.whoGo": "Identify speaker", + "voice.working": "Identifying", + "voice.whoNote": "Searches the speakers enrolled on this server. The clip you test with is not stored.", + "voice.sameNote": "The answer comes from one voiceprint per clip. Nothing is stored.", + "voice.workingText": "Comparing the clip with the voiceprints the server holds.", + "voice.probably": "Probably {{name}}", + "voice.matchText": "{{name}}'s voiceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "voice.nobody": "Nobody enrolled sounds like this", + "voice.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new speaker, or a clip that is too short or noisy.", + "voice.noneEnrolled": "Nobody is enrolled to compare with", + "voice.noneEnrolledText": "Enrol a speaker first, then identify a clip against them.", + "voice.noneReturned": "The server returned no speakers", + "voice.noneReturnedText": "It holds no voiceprints made with this model. If the server restarted, enrolled voices are gone: enrol them again.", + "voice.enrolThis": "Enrol this voice", + "voice.same": "Probably the same person", + "voice.different": "Probably different people", + "voice.knownTitle": "Known speakers", + "voice.enrol": "Enrol a speaker", + "voice.enrolTitle": "Enrol a speaker", + "voice.enrolIntro": "The server keeps a voiceprint and a name. It does not keep the recording.", + "voice.emptyTitle": "Nobody enrolled in this browser yet", + "voice.emptyText": "Enrol a speaker with a short recording, then test a clip against them.", + "voice.agreedHint": "I have their permission, or another lawful basis, to store a voiceprint of them.", + "voice.keepHint": "Lets you replay it and enrol again after a server restart. Off by default. It is stored in this browser only.", + "voice.neededTitle": "Voices need a speaker model", + "voice.neededText": "Voice recognition turns a recording into a voiceprint and compares voiceprints. It runs on a speaker-recognition model, and none is installed on this server.", + "voice.neededWhat": "Install a model that supports speaker recognition. Once one is installed, this page turns on by itself. Users also need the Voice recognition permission.", + "voice.moreTitle": "More tools", + "voice.moreHint": "guess a speaker's traits, get the raw voiceprint", + "voice.why.0": "One speaker is assumed. A clip with two people mixes both voices.", + "voice.why.1": "A different microphone or room changes how a voice sounds.", + "voice.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "voice.stored.0.where": "On the server", + "voice.stored.0.what": "A {{print}} (numbers that describe the voice, not the audio), a name and your labels. They are kept in memory, are shared by everyone who uses this server, and are lost when it restarts. The recording is not kept.", + "voice.stored.1.where": "In this browser", + "voice.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the recording only if you tick Keep a copy when enrolling.", + "voice.stored.2.where": "Clips you test with", + "voice.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "Nombre", + "known.added": "Añadido", + "known.actions": "Acciones", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "Deshacer", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "Quitar {{name}}", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "enrol.close": "Cerrar", + "enrol.cancel": "Cancelar", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Recording", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Recording", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "Nombre", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Exact transcript", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "tts.title": "Texto a voz", + "tts.labels.model": "Modelo", + "tts.labels.voice": "Voz", + "tts.labels.voicePlaceholder": "ID de voz opcional", + "tts.labels.input": "Texto", + "tts.labels.inputPlaceholder": "Introduce el texto a sintetizar...", + "tts.labels.instructions": "Instrucciones", + "tts.labels.instructionsPlaceholder": "Por ejemplo: Habla despacio y con calidez", + "tts.labels.instructionsHint": "Indicaciones opcionales sobre la locución. La compatibilidad depende del backend.", + "tts.actions.generate": "Generar audio", + "tts.actions.generating": "Generando...", + "tts.empty": "El audio generado aparecerá aquí", + "tts.toasts.noText": "Por favor introduce texto", + "tts.toasts.noModel": "Por favor selecciona un modelo", + "tts.toasts.generated": "Speech generated", + "tts.toasts.generateFailed": "Generación fallida", + "tts.voiceLibrary.cloningReady": "Voice cloning", + "tts.voiceLibrary.loading": "Loading saved personalities…", + "tts.voiceLibrary.modelDefault": "Use model default", + "tts.voiceLibrary.empty": "No saved personalities yet.", + "tts.voiceLibrary.create": "Create one", + "tts.voiceLibrary.manage": "Manage Personality Library", + "tts.voiceLibrary.namedVoiceHint": "This model does not accept reference-audio profiles. You can still enter a named speaker supported by its backend.", + "voiceLibrary.title": "Speech voices", + "voiceLibrary.subtitle": "A recording and its exact transcript. Text-to-speech models that accept reference audio can speak with it.", + "voiceLibrary.loadFailed": "Speech voices could not be loaded", + "voiceLibrary.loading": "Loading saved personalities…", + "voiceLibrary.listLabel": "Saved voice personalities", + "voiceLibrary.status.ready": "Listo", + "voiceLibrary.summary.label": "Voice library status", + "voiceLibrary.summary.profiles": "saved personalities", + "voiceLibrary.summary.modelsReady_one": "{{count}} compatible model ready", + "voiceLibrary.summary.modelsReady_other": "{{count}} compatible models ready", + "voiceLibrary.summary.noModels": "No compatible model installed", + "voiceLibrary.search.label": "Search voice profiles", + "voiceLibrary.search.placeholder": "Search voices, languages, or transcripts", + "voiceLibrary.filters.language": "Filter by language", + "voiceLibrary.filters.allLanguages": "All languages", + "voiceLibrary.empty.title": "No speech voices yet", + "voiceLibrary.empty.body": "Record or upload a consented reference clip, add its exact transcript, and reuse the voice across compatible text-to-speech models.", + "voiceLibrary.noResults.title": "No voices match these filters", + "voiceLibrary.noResults.body": "Try a different search or show every language.", + "voiceLibrary.detail.eyebrow": "Reference recording and exact transcript", + "voiceLibrary.detail.close": "Cerrar", + "voiceLibrary.detail.emptyTitle": "Select a voice to inspect it", + "voiceLibrary.detail.emptyBody": "Reference audio, transcript, compatibility, and consent details will appear here.", + "voiceLibrary.detail.referenceAudio": "Reference audio", + "voiceLibrary.detail.transcript": "Reference transcript", + "voiceLibrary.metadata.language": "Idioma", + "voiceLibrary.metadata.languageUnknown": "Not specified", + "voiceLibrary.metadata.duration": "Duration", + "voiceLibrary.metadata.sampleRate": "Sample rate", + "voiceLibrary.metadata.created": "Creado", + "voiceLibrary.consent.label": "Permission", + "voiceLibrary.consent.confirmed": "Consent confirmed", + "voiceLibrary.consent.confirmedAt": "Confirmed {{date}}", + "voiceLibrary.modelSetup.title": "Install a voice-cloning model", + "voiceLibrary.modelSetup.body": "This voice is ready, but it needs a model that accepts reference audio. Choose one verified by this server.", + "voiceLibrary.modelSetup.loading": "Finding compatible models in your galleries…", + "voiceLibrary.modelSetup.backendUnknown": "Backend selected during install", + "voiceLibrary.modelSetup.install": "Instalar", + "voiceLibrary.modelSetup.installing": "Instalando…", + "voiceLibrary.modelSetup.loadFailed": "Compatible recommendations could not be loaded.", + "voiceLibrary.modelSetup.noneAvailable": "No compatible uninstalled model is available in the configured galleries.", + "voiceLibrary.modelSetup.capabilityNote": "Recommendations come from model capability metadata, not a browser-side model list.", + "voiceLibrary.api.title": "API usage and compatible models", + "voiceLibrary.api.summary": "Use this voice outside the browser or check model support", + "voiceLibrary.api.body": "Send the profile’s stable voice URI with each speech request. LocalAI resolves the private reference audio and transcript at generation time; the settings of your models are not changed.", + "voiceLibrary.api.compatibleModels": "Compatible installed models", + "voiceLibrary.api.noInstalledModels": "None yet. Install one of the server-verified models above before sending this request.", + "voiceLibrary.api.curlExample": "cURL example", + "voiceLibrary.api.copy": "Copy request", + "voiceLibrary.api.endpointNote": "The same model, input, and voice fields also work with POST /tts.", + "voiceLibrary.actions.create": "Add a speech voice", + "voiceLibrary.actions.createFirst": "Add your first speech voice", + "voiceLibrary.actions.retry": "Reintentar", + "voiceLibrary.actions.clearFilters": "Limpiar filtros", + "voiceLibrary.actions.useInTTS": "Use in Text to Speech", + "voiceLibrary.actions.delete": "Delete voice", + "voiceLibrary.actions.installModel": "Install a compatible model", + "voiceLibrary.actions.browseModels": "Browse all models", + "voiceLibrary.deleteDialog.title": "Delete this voice?", + "voiceLibrary.deleteDialog.message": "“{{name}}” and its reference audio will be permanently removed. Models set up with this voice are not changed.", + "voiceLibrary.deleteDialog.deleting": "Deleting…", + "voiceLibrary.toasts.deleted": "Deleted {{name}}", + "voiceLibrary.toasts.installStarted": "Installing {{name}}. Progress is available in Operations.", + "voiceLibrary.toasts.installFailed": "Could not start installation: {{message}}", + "voiceLibrary.toasts.apiCopied": "API request copied", + "voiceLibrary.toasts.copyFailed": "Could not copy the API request", + "voiceCreate.title": "Add a speech voice", + "voiceCreate.subtitle": "A speech voice is one or more clean reference clips and their exact transcripts. Text-to-speech models that accept reference audio can speak with it.", + "voiceCreate.references.add": "Add another reference", + "voiceCreate.references.additional": "Reference {{number}}", + "voiceCreate.references.remove": "Quitar", + "voiceCreate.sections.reference.title": "Reference audio", + "voiceCreate.sections.reference.body": "Record in the browser or upload an existing clip. Speech should be clean, natural, and from one speaker.", + "voiceCreate.sections.details.title": "Identity and transcript", + "voiceCreate.sections.details.body": "Give admins a recognizable name and transcribe the clip exactly, including fillers and punctuation.", + "voiceCreate.sections.consent.title": "Authorization", + "voiceCreate.sections.consent.body": "Voice cloning can be sensitive. Confirm permission before this reference is saved.", + "voiceCreate.audio.label": "Voice reference", + "voiceCreate.audio.normalizing": "Preparing a secure PCM WAV…", + "voiceCreate.audio.preview": "Normalized reference", + "voiceCreate.audio.tooLarge": "This file is larger than 50 MiB.", + "voiceCreate.audio.durationError": "Reference audio must be between 1 second and 2 minutes.", + "voiceCreate.audio.decodeError": "This browser could not decode the selected audio file.", + "voiceCreate.audio.qualityReady": "Recommended length", + "voiceCreate.audio.qualityHint": "6–30 seconds usually produces a stronger clone", + "voiceCreate.fields.name": "Voice name", + "voiceCreate.fields.namePlaceholder": "e.g. Documentary narrator", + "voiceCreate.fields.language": "Language (optional)", + "voiceCreate.fields.languagePlaceholder": "e.g. en-US", + "voiceCreate.fields.description": "Description (optional)", + "voiceCreate.fields.descriptionPlaceholder": "Tone, source, or intended use", + "voiceCreate.fields.transcript": "Exact transcript", + "voiceCreate.fields.transcriptPlaceholder": "Type every word spoken in the reference clip…", + "voiceCreate.fields.transcriptHint": "Match the recording exactly. Do not rewrite grammar or remove pauses and fillers.", + "voiceCreate.consent.title": "I confirm that this voice may be cloned", + "voiceCreate.consent.body": "I have the speaker’s permission or another lawful basis to store and use this recording for synthesized speech.", + "voiceCreate.readiness.title": "Profile readiness", + "voiceCreate.readiness.body": "A voice becomes available in Text to Speech when every required item is complete.", + "voiceCreate.readiness.audio": "Valid PCM-WAV reference", + "voiceCreate.readiness.quality": "6–30 second quality window", + "voiceCreate.readiness.name": "Recognizable voice name", + "voiceCreate.readiness.transcript": "Exact reference transcript", + "voiceCreate.readiness.consent": "Authorization confirmed", + "voiceCreate.privacy.title": "Private by default. ", + "voiceCreate.privacy.body": "Reference audio is stored in LocalAI’s data directory with restricted file permissions and is never exposed as a model-folder path.", + "voiceCreate.actions.back": "Back to library", + "voiceCreate.actions.cancel": "Cancelar", + "voiceCreate.actions.save": "Save voice", + "voiceCreate.actions.saving": "Saving…", + "voiceCreate.toasts.created": "{{name}} is ready to use", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Record", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "Quitar", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "Cancelar", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "Detener", + "clip.start": "Start recording", + "clip.recorded": "Recording", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "Vista previa", + "clip.photoAlt": "The photo you chose", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "stored.title": "What is stored, and where", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "Reintentar", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "Instalar", + "needed.installing": "Instalando", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Browse all models", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "recording.title": "Name the speakers in a recording", + "recording.text": "The diarization workspace finds who spoke when in a recording. From there you can remember a speaker, and that voice joins the Speakers list here.", + "recording.step1": "Open the workspace, add a recording and tick Prepare speakers to remember.", + "recording.step2": "Run diarization. Each speaker with enough clean speech gets a Name and remember button.", + "recording.step3": "Name a speaker and remember it. That voice then appears under Speakers, ready for Who is this.", + "recording.open": "Open the diarization workspace", + "recording.off": "Diarization is not turned on for your account.", + "recording.warning": "Remembered voices are shared on this server and are lost when it restarts. Nothing is remembered automatically." +} diff --git a/addons/voice/ui/src/i18n/voice.fr.json b/addons/voice/ui/src/i18n/voice.fr.json new file mode 100644 index 0000000000..b450bf4a95 --- /dev/null +++ b/addons/voice/ui/src/i18n/voice.fr.json @@ -0,0 +1,341 @@ +{ + "voice.adminOnly.title": "Administrators only", + "voice.adminOnly.text": "Saved voices can be managed by an administrator of this server. Ask one if you need a voice added or removed.", + "speech.title": "Speak text", + "speech.lede": "Turn text into speech with a voice model of this server.", + "speech.model": "Model", + "speech.voice": "Voix", + "speech.defaultVoice": "Default voice", + "speech.text": "Texte", + "speech.instructions": "Instructions (optional)", + "speech.generate": "Generate speech", + "speech.result": "Résultat", + "speech.noModels": "No text-to-speech model is installed.", + "speech.noModelsLink": "Get one from the model gallery.", + "speech.voicePlaceholder": "Identifiant de locuteur ou de voix (facultatif)", + "speech.textPlaceholder": "What should it say?", + "speech.noAccess": "Your account has no access to text-to-speech. The server will refuse the request.", + "voices.title": "Voix", + "voices.lede": "Recognise who is speaking, and keep voices that text-to-speech can use.", + "voices.tabs": "Voices sections", + "voices.tab.recognition": "Reconnaissance", + "voices.tab.library": "Bibliothèque", + "voices.tab.speech": "Text to speech", + "voices.tab.recording": "From a recording", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "En cours", + "voice.speaker": "speaker", + "voice.print": "voiceprint", + "voice.whoTab": "Who is this", + "voice.whoLede": "Matches a clip against the people you enrolled.", + "voice.sameLede": "Two clips, one question. Nobody has to be enrolled.", + "voice.whoGo": "Identify speaker", + "voice.working": "Identifying", + "voice.whoNote": "Searches the speakers enrolled on this server. The clip you test with is not stored.", + "voice.sameNote": "The answer comes from one voiceprint per clip. Nothing is stored.", + "voice.workingText": "Comparing the clip with the voiceprints the server holds.", + "voice.probably": "Probably {{name}}", + "voice.matchText": "{{name}}'s voiceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "voice.nobody": "Nobody enrolled sounds like this", + "voice.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new speaker, or a clip that is too short or noisy.", + "voice.noneEnrolled": "Nobody is enrolled to compare with", + "voice.noneEnrolledText": "Enrol a speaker first, then identify a clip against them.", + "voice.noneReturned": "The server returned no speakers", + "voice.noneReturnedText": "It holds no voiceprints made with this model. If the server restarted, enrolled voices are gone: enrol them again.", + "voice.enrolThis": "Enrol this voice", + "voice.same": "Probably the same person", + "voice.different": "Probably different people", + "voice.knownTitle": "Known speakers", + "voice.enrol": "Enrol a speaker", + "voice.enrolTitle": "Enrol a speaker", + "voice.enrolIntro": "The server keeps a voiceprint and a name. It does not keep the recording.", + "voice.emptyTitle": "Nobody enrolled in this browser yet", + "voice.emptyText": "Enrol a speaker with a short recording, then test a clip against them.", + "voice.agreedHint": "I have their permission, or another lawful basis, to store a voiceprint of them.", + "voice.keepHint": "Lets you replay it and enrol again after a server restart. Off by default. It is stored in this browser only.", + "voice.neededTitle": "Voices need a speaker model", + "voice.neededText": "Voice recognition turns a recording into a voiceprint and compares voiceprints. It runs on a speaker-recognition model, and none is installed on this server.", + "voice.neededWhat": "Install a model that supports speaker recognition. Once one is installed, this page turns on by itself. Users also need the Voice recognition permission.", + "voice.moreTitle": "More tools", + "voice.moreHint": "guess a speaker's traits, get the raw voiceprint", + "voice.why.0": "One speaker is assumed. A clip with two people mixes both voices.", + "voice.why.1": "A different microphone or room changes how a voice sounds.", + "voice.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "voice.stored.0.where": "On the server", + "voice.stored.0.what": "A {{print}} (numbers that describe the voice, not the audio), a name and your labels. They are kept in memory, are shared by everyone who uses this server, and are lost when it restarts. The recording is not kept.", + "voice.stored.1.where": "In this browser", + "voice.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the recording only if you tick Keep a copy when enrolling.", + "voice.stored.2.where": "Clips you test with", + "voice.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "Nom", + "known.added": "Ajouté", + "known.actions": "Actions", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "Annuler", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "Retirer {{name}}", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "enrol.close": "Fermer", + "enrol.cancel": "Annuler", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Enregistrement", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Enregistrement", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "Nom", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Transcription exacte", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "tts.title": "Synthèse vocale", + "tts.labels.model": "Modèle", + "tts.labels.voice": "Voix", + "tts.labels.voicePlaceholder": "Identifiant de locuteur ou de voix (facultatif)", + "tts.labels.input": "Texte", + "tts.labels.inputPlaceholder": "Saisissez le texte à synthétiser...", + "tts.labels.instructions": "Instructions", + "tts.labels.instructionsPlaceholder": "Par exemple : Parle lentement et chaleureusement", + "tts.labels.instructionsHint": "Indications de diction facultatives. La prise en charge dépend du backend.", + "tts.actions.generate": "Générer", + "tts.actions.generating": "Génération...", + "tts.empty": "L'audio généré apparaîtra ici", + "tts.toasts.noText": "Veuillez saisir du texte", + "tts.toasts.noModel": "Veuillez sélectionner un modèle", + "tts.toasts.generated": "Parole générée", + "tts.toasts.generateFailed": "Échec de la génération", + "tts.voiceLibrary.cloningReady": "Clonage de voix", + "tts.voiceLibrary.loading": "Chargement des voix enregistrées…", + "tts.voiceLibrary.modelDefault": "Utiliser la voix par défaut du modèle", + "tts.voiceLibrary.empty": "Aucune voix enregistrée pour l'instant.", + "tts.voiceLibrary.create": "En créer une", + "tts.voiceLibrary.manage": "Gérer la bibliothèque de voix", + "tts.voiceLibrary.namedVoiceHint": "Ce modèle n'accepte pas les profils audio de référence. Vous pouvez toujours saisir un locuteur nommé pris en charge par son backend.", + "voiceLibrary.title": "Bibliothèque de voix", + "voiceLibrary.subtitle": "Créez et gérez des voix réutilisables à partir d'un ou plusieurs enregistrements de référence.", + "voiceLibrary.loadFailed": "Speech voices could not be loaded", + "voiceLibrary.loading": "Chargement des voix enregistrées…", + "voiceLibrary.listLabel": "Voix enregistrées", + "voiceLibrary.status.ready": "Prêt", + "voiceLibrary.summary.label": "État de la bibliothèque de voix", + "voiceLibrary.summary.profiles": "voix enregistrées", + "voiceLibrary.summary.modelsReady_one": "{{count}} modèle compatible prêt", + "voiceLibrary.summary.modelsReady_other": "{{count}} modèles compatibles prêts", + "voiceLibrary.summary.noModels": "Aucun modèle compatible installé", + "voiceLibrary.search.label": "Rechercher des voix", + "voiceLibrary.search.placeholder": "Rechercher par voix, langue ou transcription", + "voiceLibrary.filters.language": "Filtrer par langue", + "voiceLibrary.filters.allLanguages": "Toutes les langues", + "voiceLibrary.empty.title": "Votre bibliothèque de voix est vide", + "voiceLibrary.empty.body": "Enregistrez ou importez des extraits de référence avec consentement, ajoutez leur transcription exacte et réutilisez la voix avec les modèles TTS compatibles.", + "voiceLibrary.noResults.title": "Aucune voix ne correspond à ces filtres", + "voiceLibrary.noResults.body": "Essayez une autre recherche ou affichez toutes les langues.", + "voiceLibrary.detail.eyebrow": "Voix sélectionnée", + "voiceLibrary.detail.close": "Fermer", + "voiceLibrary.detail.emptyTitle": "Sélectionnez une voix pour l'inspecter", + "voiceLibrary.detail.emptyBody": "L'audio de référence, la transcription, la compatibilité et le consentement s'afficheront ici.", + "voiceLibrary.detail.referenceAudio": "Audio de référence", + "voiceLibrary.detail.transcript": "Transcription de référence", + "voiceLibrary.metadata.language": "Langue", + "voiceLibrary.metadata.languageUnknown": "Non précisée", + "voiceLibrary.metadata.duration": "Durée", + "voiceLibrary.metadata.sampleRate": "Fréquence d'échantillonnage", + "voiceLibrary.metadata.created": "Créée le", + "voiceLibrary.consent.label": "Permission", + "voiceLibrary.consent.confirmed": "Consentement confirmé", + "voiceLibrary.consent.confirmedAt": "Confirmé le {{date}}", + "voiceLibrary.modelSetup.title": "Installer un modèle de clonage de voix", + "voiceLibrary.modelSetup.body": "Cette voix est prête, mais elle a besoin d'un modèle acceptant l'audio de référence. Choisissez-en un vérifié par ce serveur.", + "voiceLibrary.modelSetup.loading": "Recherche de modèles compatibles dans vos galeries…", + "voiceLibrary.modelSetup.backendUnknown": "Backend choisi lors de l'installation", + "voiceLibrary.modelSetup.install": "Installer", + "voiceLibrary.modelSetup.installing": "Installation…", + "voiceLibrary.modelSetup.loadFailed": "Les recommandations compatibles n'ont pas pu être chargées.", + "voiceLibrary.modelSetup.noneAvailable": "Aucun modèle compatible non installé n'est disponible dans les galeries configurées.", + "voiceLibrary.modelSetup.capabilityNote": "Les recommandations proviennent des métadonnées de capacités des modèles, pas d'une liste côté navigateur.", + "voiceLibrary.api.title": "Utilisation de l'API et modèles compatibles", + "voiceLibrary.api.summary": "Utilisez cette voix hors du navigateur ou vérifiez la compatibilité des modèles", + "voiceLibrary.api.body": "Envoyez l'URI stable de la voix avec chaque requête de synthèse. LocalAI résout l'audio de référence privé et sa transcription au moment de la génération ; les réglages de vos modèles ne sont pas modifiés.", + "voiceLibrary.api.compatibleModels": "Modèles installés compatibles", + "voiceLibrary.api.noInstalledModels": "Aucun pour l'instant. Installez l'un des modèles vérifiés ci-dessus avant d'envoyer cette requête.", + "voiceLibrary.api.curlExample": "Exemple cURL", + "voiceLibrary.api.copy": "Copier la requête", + "voiceLibrary.api.endpointNote": "Les mêmes champs model, input et voice fonctionnent aussi avec POST /tts.", + "voiceLibrary.actions.create": "Créer une voix", + "voiceLibrary.actions.createFirst": "Créer votre première voix", + "voiceLibrary.actions.retry": "Réessayer", + "voiceLibrary.actions.clearFilters": "Effacer les filtres", + "voiceLibrary.actions.useInTTS": "Utiliser en synthèse vocale", + "voiceLibrary.actions.delete": "Supprimer la voix", + "voiceLibrary.actions.installModel": "Installer un modèle compatible", + "voiceLibrary.actions.browseModels": "Parcourir tous les modèles", + "voiceLibrary.deleteDialog.title": "Supprimer cette voix ?", + "voiceLibrary.deleteDialog.message": "« {{name}} » et son audio de référence seront définitivement supprimés. Les modèles configurés avec cette voix ne sont pas modifiés.", + "voiceLibrary.deleteDialog.deleting": "Suppression…", + "voiceLibrary.toasts.deleted": "{{name}} supprimée", + "voiceLibrary.toasts.installStarted": "Installation de {{name}}. La progression est disponible dans Opérations.", + "voiceLibrary.toasts.installFailed": "Impossible de démarrer l'installation : {{message}}", + "voiceLibrary.toasts.apiCopied": "Requête API copiée", + "voiceLibrary.toasts.copyFailed": "Impossible de copier la requête API", + "voiceCreate.title": "Créer une voix réutilisable", + "voiceCreate.subtitle": "Créez une voix réutilisable à partir d'un ou plusieurs extraits de référence propres et de leur transcription exacte.", + "voiceCreate.references.add": "Ajouter une autre référence", + "voiceCreate.references.additional": "Référence {{number}}", + "voiceCreate.references.remove": "Retirer", + "voiceCreate.sections.reference.title": "Audio de référence", + "voiceCreate.sections.reference.body": "Enregistrez dans le navigateur ou importez un extrait existant. La parole doit être propre, naturelle et provenir d'un seul locuteur.", + "voiceCreate.sections.details.title": "Identité et transcription", + "voiceCreate.sections.details.body": "Donnez un nom reconnaissable aux administrateurs et transcrivez l'extrait exactement, hésitations et ponctuation comprises.", + "voiceCreate.sections.consent.title": "Autorisation", + "voiceCreate.sections.consent.body": "Le clonage de voix peut être sensible. Confirmez l'autorisation avant d'enregistrer cette référence.", + "voiceCreate.audio.label": "Référence vocale", + "voiceCreate.audio.normalizing": "Préparation d'un WAV PCM sécurisé…", + "voiceCreate.audio.preview": "Référence normalisée", + "voiceCreate.audio.tooLarge": "This file is larger than 50 MiB.", + "voiceCreate.audio.durationError": "L'audio de référence doit durer entre 1 seconde et 2 minutes.", + "voiceCreate.audio.decodeError": "Ce navigateur n'a pas pu décoder le fichier audio sélectionné.", + "voiceCreate.audio.qualityReady": "Durée recommandée", + "voiceCreate.audio.qualityHint": "6 à 30 secondes produisent généralement un meilleur clone", + "voiceCreate.fields.name": "Nom de la voix", + "voiceCreate.fields.namePlaceholder": "ex. Narrateur documentaire", + "voiceCreate.fields.language": "Langue (facultatif)", + "voiceCreate.fields.languagePlaceholder": "ex. fr-FR", + "voiceCreate.fields.description": "Description (facultatif)", + "voiceCreate.fields.descriptionPlaceholder": "Ton, source ou usage prévu", + "voiceCreate.fields.transcript": "Transcription exacte", + "voiceCreate.fields.transcriptPlaceholder": "Saisissez chaque mot prononcé dans l'extrait…", + "voiceCreate.fields.transcriptHint": "Reproduisez l'enregistrement exactement. Ne corrigez pas la grammaire et ne supprimez ni pauses ni hésitations.", + "voiceCreate.consent.title": "Je confirme que cette voix peut être clonée", + "voiceCreate.consent.body": "J'ai l'autorisation du locuteur ou une autre base légale pour stocker et utiliser cet enregistrement afin de produire de la parole synthétique.", + "voiceCreate.readiness.title": "Préparation de la voix", + "voiceCreate.readiness.body": "Une voix devient disponible en synthèse vocale lorsque tous les éléments requis sont remplis.", + "voiceCreate.readiness.audio": "Référence PCM-WAV valide", + "voiceCreate.readiness.quality": "Fenêtre de qualité de 6 à 30 secondes", + "voiceCreate.readiness.name": "Nom de voix reconnaissable", + "voiceCreate.readiness.transcript": "Transcription de référence exacte", + "voiceCreate.readiness.consent": "Autorisation confirmée", + "voiceCreate.privacy.title": "Privé par défaut. ", + "voiceCreate.privacy.body": "L'audio de référence est stocké dans le répertoire de données de LocalAI avec des permissions restreintes et n'est jamais exposé comme chemin de dossier de modèle.", + "voiceCreate.actions.back": "Retour à la bibliothèque", + "voiceCreate.actions.cancel": "Annuler", + "voiceCreate.actions.save": "Enregistrer la voix", + "voiceCreate.actions.saving": "Enregistrement…", + "voiceCreate.toasts.created": "{{name}} est prête à l'emploi", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Enregistrer", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "Retirer", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "Annuler", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "Arrêter", + "clip.start": "Démarrer l'enregistrement", + "clip.recorded": "Enregistrement", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "Aperçu", + "clip.photoAlt": "The photo you chose", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "stored.title": "What is stored, and where", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "Réessayer", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "Installer", + "needed.installing": "Installation en cours", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Parcourir tous les modèles", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "recording.title": "Name the speakers in a recording", + "recording.text": "The diarization workspace finds who spoke when in a recording. From there you can remember a speaker, and that voice joins the Speakers list here.", + "recording.step1": "Open the workspace, add a recording and tick Prepare speakers to remember.", + "recording.step2": "Run diarization. Each speaker with enough clean speech gets a Name and remember button.", + "recording.step3": "Name a speaker and remember it. That voice then appears under Speakers, ready for Who is this.", + "recording.open": "Open the diarization workspace", + "recording.off": "Diarization is not turned on for your account.", + "recording.warning": "Remembered voices are shared on this server and are lost when it restarts. Nothing is remembered automatically." +} diff --git a/addons/voice/ui/src/i18n/voice.id.json b/addons/voice/ui/src/i18n/voice.id.json new file mode 100644 index 0000000000..b3c7d9fb49 --- /dev/null +++ b/addons/voice/ui/src/i18n/voice.id.json @@ -0,0 +1,341 @@ +{ + "voice.adminOnly.title": "Administrators only", + "voice.adminOnly.text": "Saved voices can be managed by an administrator of this server. Ask one if you need a voice added or removed.", + "speech.title": "Speak text", + "speech.lede": "Turn text into speech with a voice model of this server.", + "speech.model": "Model", + "speech.voice": "Suara", + "speech.defaultVoice": "Default voice", + "speech.text": "Teks", + "speech.instructions": "Instructions (optional)", + "speech.generate": "Generate speech", + "speech.result": "Hasil", + "speech.noModels": "No text-to-speech model is installed.", + "speech.noModelsLink": "Get one from the model gallery.", + "speech.voicePlaceholder": "ID suara opsional", + "speech.textPlaceholder": "What should it say?", + "speech.noAccess": "Your account has no access to text-to-speech. The server will refuse the request.", + "voices.title": "Suara", + "voices.lede": "Recognise who is speaking, and keep voices that text-to-speech can use.", + "voices.tabs": "Voices sections", + "voices.tab.recognition": "Pengenalan", + "voices.tab.library": "Pustaka", + "voices.tab.speech": "Text to speech", + "voices.tab.recording": "From a recording", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "Sedang dikerjakan", + "voice.speaker": "speaker", + "voice.print": "voiceprint", + "voice.whoTab": "Who is this", + "voice.whoLede": "Matches a clip against the people you enrolled.", + "voice.sameLede": "Two clips, one question. Nobody has to be enrolled.", + "voice.whoGo": "Identify speaker", + "voice.working": "Identifying", + "voice.whoNote": "Searches the speakers enrolled on this server. The clip you test with is not stored.", + "voice.sameNote": "The answer comes from one voiceprint per clip. Nothing is stored.", + "voice.workingText": "Comparing the clip with the voiceprints the server holds.", + "voice.probably": "Probably {{name}}", + "voice.matchText": "{{name}}'s voiceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "voice.nobody": "Nobody enrolled sounds like this", + "voice.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new speaker, or a clip that is too short or noisy.", + "voice.noneEnrolled": "Nobody is enrolled to compare with", + "voice.noneEnrolledText": "Enrol a speaker first, then identify a clip against them.", + "voice.noneReturned": "The server returned no speakers", + "voice.noneReturnedText": "It holds no voiceprints made with this model. If the server restarted, enrolled voices are gone: enrol them again.", + "voice.enrolThis": "Enrol this voice", + "voice.same": "Probably the same person", + "voice.different": "Probably different people", + "voice.knownTitle": "Known speakers", + "voice.enrol": "Enrol a speaker", + "voice.enrolTitle": "Enrol a speaker", + "voice.enrolIntro": "The server keeps a voiceprint and a name. It does not keep the recording.", + "voice.emptyTitle": "Nobody enrolled in this browser yet", + "voice.emptyText": "Enrol a speaker with a short recording, then test a clip against them.", + "voice.agreedHint": "I have their permission, or another lawful basis, to store a voiceprint of them.", + "voice.keepHint": "Lets you replay it and enrol again after a server restart. Off by default. It is stored in this browser only.", + "voice.neededTitle": "Voices need a speaker model", + "voice.neededText": "Voice recognition turns a recording into a voiceprint and compares voiceprints. It runs on a speaker-recognition model, and none is installed on this server.", + "voice.neededWhat": "Install a model that supports speaker recognition. Once one is installed, this page turns on by itself. Users also need the Voice recognition permission.", + "voice.moreTitle": "More tools", + "voice.moreHint": "guess a speaker's traits, get the raw voiceprint", + "voice.why.0": "One speaker is assumed. A clip with two people mixes both voices.", + "voice.why.1": "A different microphone or room changes how a voice sounds.", + "voice.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "voice.stored.0.where": "On the server", + "voice.stored.0.what": "A {{print}} (numbers that describe the voice, not the audio), a name and your labels. They are kept in memory, are shared by everyone who uses this server, and are lost when it restarts. The recording is not kept.", + "voice.stored.1.where": "In this browser", + "voice.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the recording only if you tick Keep a copy when enrolling.", + "voice.stored.2.where": "Clips you test with", + "voice.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "Nama", + "known.added": "Ditambahkan", + "known.actions": "Aksi", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "Urungkan", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "Hapus {{name}}", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "enrol.close": "Tutup", + "enrol.cancel": "Batal", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Recording", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Recording", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "Nama", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Exact transcript", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "tts.title": "Teks ke Suara", + "tts.labels.model": "Model", + "tts.labels.voice": "Suara", + "tts.labels.voicePlaceholder": "ID suara opsional", + "tts.labels.input": "Teks", + "tts.labels.inputPlaceholder": "Masukkan teks untuk disintesis...", + "tts.labels.instructions": "Instruksi", + "tts.labels.instructionsPlaceholder": "Contoh: Ucapkan perlahan dan hangat", + "tts.labels.instructionsHint": "Panduan penyampaian opsional. Dukungan bergantung pada backend.", + "tts.actions.generate": "Hasilkan", + "tts.actions.generating": "Menghasilkan...", + "tts.empty": "Audio yang dihasilkan akan muncul di sini", + "tts.toasts.noText": "Silahkan masukkan teks", + "tts.toasts.noModel": "Silahkan pilih model", + "tts.toasts.generated": "Speech generated", + "tts.toasts.generateFailed": "Gagal menghasilkan", + "tts.voiceLibrary.cloningReady": "Voice cloning", + "tts.voiceLibrary.loading": "Loading saved personalities…", + "tts.voiceLibrary.modelDefault": "Use model default", + "tts.voiceLibrary.empty": "No saved personalities yet.", + "tts.voiceLibrary.create": "Create one", + "tts.voiceLibrary.manage": "Manage Personality Library", + "tts.voiceLibrary.namedVoiceHint": "This model does not accept reference-audio profiles. You can still enter a named speaker supported by its backend.", + "voiceLibrary.title": "Speech voices", + "voiceLibrary.subtitle": "A recording and its exact transcript. Text-to-speech models that accept reference audio can speak with it.", + "voiceLibrary.loadFailed": "Speech voices could not be loaded", + "voiceLibrary.loading": "Loading saved personalities…", + "voiceLibrary.listLabel": "Saved voice personalities", + "voiceLibrary.status.ready": "Siap", + "voiceLibrary.summary.label": "Voice library status", + "voiceLibrary.summary.profiles": "saved personalities", + "voiceLibrary.summary.modelsReady_one": "{{count}} compatible model ready", + "voiceLibrary.summary.modelsReady_other": "{{count}} compatible models ready", + "voiceLibrary.summary.noModels": "No compatible model installed", + "voiceLibrary.search.label": "Search voice profiles", + "voiceLibrary.search.placeholder": "Search voices, languages, or transcripts", + "voiceLibrary.filters.language": "Filter by language", + "voiceLibrary.filters.allLanguages": "All languages", + "voiceLibrary.empty.title": "No speech voices yet", + "voiceLibrary.empty.body": "Record or upload a consented reference clip, add its exact transcript, and reuse the voice across compatible text-to-speech models.", + "voiceLibrary.noResults.title": "No voices match these filters", + "voiceLibrary.noResults.body": "Try a different search or show every language.", + "voiceLibrary.detail.eyebrow": "Reference recording and exact transcript", + "voiceLibrary.detail.close": "Tutup", + "voiceLibrary.detail.emptyTitle": "Select a voice to inspect it", + "voiceLibrary.detail.emptyBody": "Reference audio, transcript, compatibility, and consent details will appear here.", + "voiceLibrary.detail.referenceAudio": "Reference audio", + "voiceLibrary.detail.transcript": "Reference transcript", + "voiceLibrary.metadata.language": "Bahasa", + "voiceLibrary.metadata.languageUnknown": "Not specified", + "voiceLibrary.metadata.duration": "Duration", + "voiceLibrary.metadata.sampleRate": "Sample rate", + "voiceLibrary.metadata.created": "Membuat", + "voiceLibrary.consent.label": "Permission", + "voiceLibrary.consent.confirmed": "Consent confirmed", + "voiceLibrary.consent.confirmedAt": "Confirmed {{date}}", + "voiceLibrary.modelSetup.title": "Install a voice-cloning model", + "voiceLibrary.modelSetup.body": "This voice is ready, but it needs a model that accepts reference audio. Choose one verified by this server.", + "voiceLibrary.modelSetup.loading": "Finding compatible models in your galleries…", + "voiceLibrary.modelSetup.backendUnknown": "Backend selected during install", + "voiceLibrary.modelSetup.install": "Pasang", + "voiceLibrary.modelSetup.installing": "Memasang…", + "voiceLibrary.modelSetup.loadFailed": "Compatible recommendations could not be loaded.", + "voiceLibrary.modelSetup.noneAvailable": "No compatible uninstalled model is available in the configured galleries.", + "voiceLibrary.modelSetup.capabilityNote": "Recommendations come from model capability metadata, not a browser-side model list.", + "voiceLibrary.api.title": "API usage and compatible models", + "voiceLibrary.api.summary": "Use this voice outside the browser or check model support", + "voiceLibrary.api.body": "Send the profile’s stable voice URI with each speech request. LocalAI resolves the private reference audio and transcript at generation time; the settings of your models are not changed.", + "voiceLibrary.api.compatibleModels": "Compatible installed models", + "voiceLibrary.api.noInstalledModels": "None yet. Install one of the server-verified models above before sending this request.", + "voiceLibrary.api.curlExample": "cURL example", + "voiceLibrary.api.copy": "Copy request", + "voiceLibrary.api.endpointNote": "The same model, input, and voice fields also work with POST /tts.", + "voiceLibrary.actions.create": "Add a speech voice", + "voiceLibrary.actions.createFirst": "Add your first speech voice", + "voiceLibrary.actions.retry": "Coba lagi", + "voiceLibrary.actions.clearFilters": "Hapus filter", + "voiceLibrary.actions.useInTTS": "Use in Text to Speech", + "voiceLibrary.actions.delete": "Delete voice", + "voiceLibrary.actions.installModel": "Install a compatible model", + "voiceLibrary.actions.browseModels": "Browse all models", + "voiceLibrary.deleteDialog.title": "Delete this voice?", + "voiceLibrary.deleteDialog.message": "“{{name}}” and its reference audio will be permanently removed. Models set up with this voice are not changed.", + "voiceLibrary.deleteDialog.deleting": "Deleting…", + "voiceLibrary.toasts.deleted": "Deleted {{name}}", + "voiceLibrary.toasts.installStarted": "Installing {{name}}. Progress is available in Operations.", + "voiceLibrary.toasts.installFailed": "Could not start installation: {{message}}", + "voiceLibrary.toasts.apiCopied": "API request copied", + "voiceLibrary.toasts.copyFailed": "Could not copy the API request", + "voiceCreate.title": "Add a speech voice", + "voiceCreate.subtitle": "A speech voice is one or more clean reference clips and their exact transcripts. Text-to-speech models that accept reference audio can speak with it.", + "voiceCreate.references.add": "Add another reference", + "voiceCreate.references.additional": "Reference {{number}}", + "voiceCreate.references.remove": "Hapus", + "voiceCreate.sections.reference.title": "Reference audio", + "voiceCreate.sections.reference.body": "Record in the browser or upload an existing clip. Speech should be clean, natural, and from one speaker.", + "voiceCreate.sections.details.title": "Identity and transcript", + "voiceCreate.sections.details.body": "Give admins a recognizable name and transcribe the clip exactly, including fillers and punctuation.", + "voiceCreate.sections.consent.title": "Authorization", + "voiceCreate.sections.consent.body": "Voice cloning can be sensitive. Confirm permission before this reference is saved.", + "voiceCreate.audio.label": "Voice reference", + "voiceCreate.audio.normalizing": "Preparing a secure PCM WAV…", + "voiceCreate.audio.preview": "Normalized reference", + "voiceCreate.audio.tooLarge": "This file is larger than 50 MiB.", + "voiceCreate.audio.durationError": "Reference audio must be between 1 second and 2 minutes.", + "voiceCreate.audio.decodeError": "This browser could not decode the selected audio file.", + "voiceCreate.audio.qualityReady": "Recommended length", + "voiceCreate.audio.qualityHint": "6–30 seconds usually produces a stronger clone", + "voiceCreate.fields.name": "Voice name", + "voiceCreate.fields.namePlaceholder": "e.g. Documentary narrator", + "voiceCreate.fields.language": "Language (optional)", + "voiceCreate.fields.languagePlaceholder": "e.g. en-US", + "voiceCreate.fields.description": "Description (optional)", + "voiceCreate.fields.descriptionPlaceholder": "Tone, source, or intended use", + "voiceCreate.fields.transcript": "Exact transcript", + "voiceCreate.fields.transcriptPlaceholder": "Type every word spoken in the reference clip…", + "voiceCreate.fields.transcriptHint": "Match the recording exactly. Do not rewrite grammar or remove pauses and fillers.", + "voiceCreate.consent.title": "I confirm that this voice may be cloned", + "voiceCreate.consent.body": "I have the speaker’s permission or another lawful basis to store and use this recording for synthesized speech.", + "voiceCreate.readiness.title": "Profile readiness", + "voiceCreate.readiness.body": "A voice becomes available in Text to Speech when every required item is complete.", + "voiceCreate.readiness.audio": "Valid PCM-WAV reference", + "voiceCreate.readiness.quality": "6–30 second quality window", + "voiceCreate.readiness.name": "Recognizable voice name", + "voiceCreate.readiness.transcript": "Exact reference transcript", + "voiceCreate.readiness.consent": "Authorization confirmed", + "voiceCreate.privacy.title": "Private by default. ", + "voiceCreate.privacy.body": "Reference audio is stored in LocalAI’s data directory with restricted file permissions and is never exposed as a model-folder path.", + "voiceCreate.actions.back": "Back to library", + "voiceCreate.actions.cancel": "Batal", + "voiceCreate.actions.save": "Save voice", + "voiceCreate.actions.saving": "Saving…", + "voiceCreate.toasts.created": "{{name}} is ready to use", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Rekam", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "Hapus", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "Batal", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "Berhenti", + "clip.start": "Mulai merekam", + "clip.recorded": "Recording", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "Pratinjau", + "clip.photoAlt": "The photo you chose", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "stored.title": "What is stored, and where", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "Coba lagi", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "Pasang", + "needed.installing": "Memasang", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Browse all models", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "recording.title": "Name the speakers in a recording", + "recording.text": "The diarization workspace finds who spoke when in a recording. From there you can remember a speaker, and that voice joins the Speakers list here.", + "recording.step1": "Open the workspace, add a recording and tick Prepare speakers to remember.", + "recording.step2": "Run diarization. Each speaker with enough clean speech gets a Name and remember button.", + "recording.step3": "Name a speaker and remember it. That voice then appears under Speakers, ready for Who is this.", + "recording.open": "Open the diarization workspace", + "recording.off": "Diarization is not turned on for your account.", + "recording.warning": "Remembered voices are shared on this server and are lost when it restarts. Nothing is remembered automatically." +} diff --git a/addons/voice/ui/src/i18n/voice.it.json b/addons/voice/ui/src/i18n/voice.it.json new file mode 100644 index 0000000000..fb3e29241c --- /dev/null +++ b/addons/voice/ui/src/i18n/voice.it.json @@ -0,0 +1,341 @@ +{ + "voice.adminOnly.title": "Solo per amministratori", + "voice.adminOnly.text": "Le voci salvate possono essere gestite da un amministratore di questo server. Chiedine uno se devi aggiungere o rimuovere una voce.", + "speech.title": "Pronuncia un testo", + "speech.lede": "Trasforma un testo in voce con un modello vocale di questo server.", + "speech.model": "Model", + "speech.voice": "Voice", + "speech.defaultVoice": "Voce predefinita", + "speech.text": "Testo", + "speech.instructions": "Istruzioni (facoltative)", + "speech.generate": "Genera la voce", + "speech.result": "Risultato", + "speech.noModels": "Non è installato nessun modello di sintesi vocale.", + "speech.noModelsLink": "Scegline uno dalla galleria dei modelli.", + "speech.voicePlaceholder": "ID voce opzionale", + "speech.textPlaceholder": "Cosa deve dire?", + "speech.noAccess": "Il tuo account non ha accesso alla sintesi vocale. Il server rifiuterà la richiesta.", + "voices.title": "Voci", + "voices.lede": "Riconosci chi sta parlando e conserva le voci che la sintesi vocale può usare.", + "voices.tabs": "Sezioni delle voci", + "voices.tab.recognition": "Riconoscimento", + "voices.tab.library": "Libreria", + "voices.tab.speech": "Sintesi vocale", + "voices.tab.recording": "Da una registrazione", + "tool.aria": "Strumento di riconoscimento", + "tool.sameTab": "Stessa persona?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Foto", + "tool.photoA": "Foto A", + "tool.photoB": "Foto B", + "tool.compare": "Confronta", + "tool.comparing": "Confronto in corso", + "tool.workingAria": "In corso", + "voice.speaker": "parlante", + "voice.print": "impronta vocale", + "voice.whoTab": "Chi è", + "voice.whoLede": "Confronta una clip con le persone che hai registrato.", + "voice.sameLede": "Due clip, una domanda. Non serve aver registrato nessuno.", + "voice.whoGo": "Identifica il parlante", + "voice.working": "Identificazione in corso", + "voice.whoNote": "Cerca tra i parlanti registrati su questo server. La clip che usi per il test non viene conservata.", + "voice.sameNote": "La risposta viene da una sola impronta vocale per clip. Non viene conservato nulla.", + "voice.workingText": "Confronto della clip con le impronte vocali che il server conserva.", + "voice.probably": "Probabilmente {{name}}", + "voice.matchText": "L'impronta vocale di {{name}} è la più vicina tra {{count}} confrontate, a distanza {{distance}}. Sotto {{cutoff}} si considera la stessa persona.", + "voice.nobody": "Nessuno dei registrati ha questa voce", + "voice.missText": "La più vicina era {{name}} a distanza {{distance}}, oltre la soglia di {{cutoff}}. Di solito significa un parlante nuovo, oppure una clip troppo breve o rumorosa.", + "voice.noneEnrolled": "Non c'è nessuno registrato con cui confrontare", + "voice.noneEnrolledText": "Registra prima un parlante, poi identifica una clip rispetto a lui.", + "voice.noneReturned": "Il server non ha restituito nessun parlante", + "voice.noneReturnedText": "Non conserva impronte vocali create con questo modello. Se il server è stato riavviato, le voci registrate sono perse: registrale di nuovo.", + "voice.enrolThis": "Registra questa voce", + "voice.same": "Probabilmente la stessa persona", + "voice.different": "Probabilmente persone diverse", + "voice.knownTitle": "Parlanti noti", + "voice.enrol": "Registra un parlante", + "voice.enrolTitle": "Registra un parlante", + "voice.enrolIntro": "Il server conserva un'impronta vocale e un nome. Non conserva la registrazione.", + "voice.emptyTitle": "Nessuno è ancora registrato in questo browser", + "voice.emptyText": "Registra un parlante con una breve registrazione, poi prova una clip rispetto a lui.", + "voice.agreedHint": "Ho il suo permesso, o un'altra base legittima, per conservare la sua impronta vocale.", + "voice.keepHint": "Ti permette di riascoltarla e di registrare di nuovo dopo il riavvio del server. Disattivato per impostazione predefinita. Resta solo in questo browser.", + "voice.neededTitle": "Le voci richiedono un modello per i parlanti", + "voice.neededText": "Il riconoscimento vocale trasforma una registrazione in un'impronta vocale e confronta le impronte. Funziona con un modello di riconoscimento del parlante, e su questo server non ne è installato nessuno.", + "voice.neededWhat": "Installa un modello che supporti il riconoscimento del parlante. Appena ne è installato uno, questa pagina si attiva da sola. Gli utenti hanno bisogno anche del permesso di riconoscimento vocale.", + "voice.moreTitle": "Altri strumenti", + "voice.moreHint": "stima le caratteristiche di un parlante, ottieni l'impronta vocale grezza", + "voice.why.0": "Si presume un solo parlante. Una clip con due persone mescola le due voci.", + "voice.why.1": "Un altro microfono o un'altra stanza cambia il suono di una voce.", + "voice.why.2": "È un punteggio di somiglianza, non un controllo di sicurezza. Non usarlo per sbloccare nulla.", + "voice.stored.0.where": "Sul server", + "voice.stored.0.what": "Un'{{print}} (numeri che descrivono la voce, non l'audio), un nome e le tue etichette. Restano in memoria, sono condivise da tutti quelli che usano questo server e si perdono al riavvio. La registrazione non viene conservata.", + "voice.stored.1.where": "In questo browser", + "voice.stored.1.what": "Nome, etichette e ora di ogni persona che hai registrato. Una copia della registrazione solo se spunti Conserva una copia durante la registrazione.", + "voice.stored.2.where": "Clip che usi per il test", + "voice.stored.2.what": "Vanno al modello su questo server per essere confrontate e non vengono conservate.", + "cutoff.label": "Soglia", + "cutoff.default": "predefinita del modello", + "cutoff.reset": "Ripristina a {{value}}", + "cutoff.stricter": "Più severa", + "cutoff.looser": "Più larga", + "cutoff.explain": "Più severa significa meno corrispondenze sbagliate e più mancate. La risposta qui sopra si aggiorna mentre la sposti. Il server non viene interpellato di nuovo.", + "why.title": "Perché potrebbe essere sbagliato", + "why.short": "La clip è breve: {{s}} s. Funziona meglio con almeno tre secondi di voce.", + "known.caption": "Persone salvate in questo browser, con lo stato della loro {{noun}}", + "known.name": "Nome", + "known.added": "Aggiunto", + "known.actions": "Azioni", + "known.best": "corrispondenza migliore", + "known.notOnServer": "non sul server", + "known.notInBrowser": "non nell'elenco di questo browser", + "known.removing": "rimozione", + "known.goneText": "L'ultima ricerca non ha restituito questa persona. Il server potrebbe essere stato riavviato, quindi l'{{print}} è persa, oppure è stata registrata con un altro modello.", + "known.extraText": "Il server ha restituito questa persona, ma questo browser non ne ha traccia.", + "known.noLabels": "nessuna etichetta", + "known.undo": "Annulla", + "known.reenrolSaved": "Registra di nuovo dalla copia salvata", + "known.enrolAgain": "Registra di nuovo", + "known.play": "Riproduci il campione salvato di {{name}}", + "known.forget": "Rimuovi {{name}}", + "known.note": "Questo elenco è conservato in questo browser. Il server non ha una chiamata per elencare, quindi non si può verificare finché non esegui una ricerca.", + "known.noteSearched": "Questo elenco è conservato in questo browser. L'ultima ricerca lo ha confrontato con il server; una persona che non è stata restituita è segnalata.", + "enrol.close": "Chiudi", + "enrol.cancel": "Annulla", + "enrol.submit": "Registra", + "enrol.working": "Registrazione in corso", + "enrol.ready": "Pronto per registrare.", + "enrol.stillNeeded": "Manca ancora: {{list}}.", + "enrol.needModel": "un modello", + "enrol.needRecording": "una registrazione", + "enrol.needPhoto": "una foto", + "enrol.needName": "un nome", + "enrol.needTranscript": "una trascrizione", + "enrol.needPermission": "il permesso", + "enrol.recordingStep": "Registrazione", + "enrol.photoStep": "Foto", + "enrol.recordingLabel": "Registrazione", + "enrol.photoLabel": "Foto", + "enrol.whoStep": "Chi è", + "enrol.name": "Nome", + "enrol.namePlaceholder": "es. Marta Rossi", + "enrol.labels": "Etichette", + "enrol.labelsHint": "facoltative, una per riga come chiave: valore", + "enrol.asSpeech": "Conserva anche come voce di sintesi", + "enrol.asSpeechHint": "La sintesi vocale potrà poi parlare con questa voce. Serve la trascrizione esatta.", + "enrol.transcript": "Trascrizione esatta", + "enrol.permissionStep": "Permesso", + "enrol.agreed": "Questa persona ha acconsentito", + "enrol.keep": "Conserva una copia in questo browser", + "tts.title": "Sintesi vocale", + "tts.labels.model": "Modello", + "tts.labels.voice": "Voce", + "tts.labels.voicePlaceholder": "ID voce opzionale", + "tts.labels.input": "Testo", + "tts.labels.inputPlaceholder": "Inserisci il testo da convertire in voce...", + "tts.labels.instructions": "Istruzioni", + "tts.labels.instructionsPlaceholder": "Ad esempio: Parla lentamente e con calore", + "tts.labels.instructionsHint": "Indicazioni facoltative sullo stile. Il supporto dipende dal backend.", + "tts.actions.generate": "Genera audio", + "tts.actions.generating": "Generazione...", + "tts.empty": "L'audio generato apparirà qui", + "tts.toasts.noText": "Inserisci del testo", + "tts.toasts.noModel": "Seleziona un modello", + "tts.toasts.generated": "Voce generata", + "tts.toasts.generateFailed": "Generazione fallita", + "tts.voiceLibrary.cloningReady": "Clonazione vocale", + "tts.voiceLibrary.loading": "Caricamento delle personalità salvate…", + "tts.voiceLibrary.modelDefault": "Usa il predefinito del modello", + "tts.voiceLibrary.empty": "Nessuna personalità salvata finora.", + "tts.voiceLibrary.create": "Creane una", + "tts.voiceLibrary.manage": "Gestisci la libreria delle personalità", + "tts.voiceLibrary.namedVoiceHint": "Questo modello non accetta profili con audio di riferimento. Puoi comunque inserire un parlante con nome supportato dal suo backend.", + "voiceLibrary.title": "Voci di sintesi", + "voiceLibrary.subtitle": "Una registrazione e la sua trascrizione esatta. I modelli di sintesi vocale che accettano audio di riferimento possono parlare con essa.", + "voiceLibrary.loadFailed": "Non è stato possibile caricare le voci di sintesi", + "voiceLibrary.loading": "Caricamento delle personalità salvate…", + "voiceLibrary.listLabel": "Personalità vocali salvate", + "voiceLibrary.status.ready": "Pronto", + "voiceLibrary.summary.label": "Stato della libreria vocale", + "voiceLibrary.summary.profiles": "personalità salvate", + "voiceLibrary.summary.modelsReady_one": "{{count}} modello compatibile pronto", + "voiceLibrary.summary.modelsReady_other": "{{count}} modelli compatibili pronti", + "voiceLibrary.summary.noModels": "Nessun modello compatibile installato", + "voiceLibrary.search.label": "Cerca profili vocali", + "voiceLibrary.search.placeholder": "Cerca voci, lingue o trascrizioni", + "voiceLibrary.filters.language": "Filtra per lingua", + "voiceLibrary.filters.allLanguages": "Tutte le lingue", + "voiceLibrary.empty.title": "Ancora nessuna voce di sintesi", + "voiceLibrary.empty.body": "Registra o carica una clip di riferimento autorizzata, aggiungi la sua trascrizione esatta e riusa la voce con i modelli di sintesi vocale compatibili.", + "voiceLibrary.noResults.title": "Nessuna voce corrisponde a questi filtri", + "voiceLibrary.noResults.body": "Prova un'altra ricerca o mostra tutte le lingue.", + "voiceLibrary.detail.eyebrow": "Registrazione di riferimento e trascrizione esatta", + "voiceLibrary.detail.close": "Chiudi", + "voiceLibrary.detail.emptyTitle": "Seleziona una voce per esaminarla", + "voiceLibrary.detail.emptyBody": "Qui compariranno audio di riferimento, trascrizione, compatibilità e dettagli sul consenso.", + "voiceLibrary.detail.referenceAudio": "Audio di riferimento", + "voiceLibrary.detail.transcript": "Trascrizione di riferimento", + "voiceLibrary.metadata.language": "Lingua", + "voiceLibrary.metadata.languageUnknown": "Non specificata", + "voiceLibrary.metadata.duration": "Durata", + "voiceLibrary.metadata.sampleRate": "Frequenza di campionamento", + "voiceLibrary.metadata.created": "Creato", + "voiceLibrary.consent.label": "Permesso", + "voiceLibrary.consent.confirmed": "Consenso confermato", + "voiceLibrary.consent.confirmedAt": "Confermato il {{date}}", + "voiceLibrary.modelSetup.title": "Installa un modello di clonazione vocale", + "voiceLibrary.modelSetup.body": "Questa voce è pronta, ma serve un modello che accetti audio di riferimento. Scegline uno verificato da questo server.", + "voiceLibrary.modelSetup.loading": "Ricerca dei modelli compatibili nelle tue gallerie…", + "voiceLibrary.modelSetup.backendUnknown": "Backend scelto durante l'installazione", + "voiceLibrary.modelSetup.install": "Installa", + "voiceLibrary.modelSetup.installing": "Installazione…", + "voiceLibrary.modelSetup.loadFailed": "Non è stato possibile caricare i consigli compatibili.", + "voiceLibrary.modelSetup.noneAvailable": "Nelle gallerie configurate non c'è nessun modello compatibile da installare.", + "voiceLibrary.modelSetup.capabilityNote": "I consigli vengono dai metadati delle capacità dei modelli, non da un elenco di modelli nel browser.", + "voiceLibrary.api.title": "Uso dell'API e modelli compatibili", + "voiceLibrary.api.summary": "Usa questa voce fuori dal browser o verifica il supporto dei modelli", + "voiceLibrary.api.body": "Invia l'URI stabile della voce del profilo con ogni richiesta di sintesi. LocalAI risolve l'audio di riferimento privato e la trascrizione al momento della generazione; le impostazioni dei tuoi modelli non cambiano.", + "voiceLibrary.api.compatibleModels": "Modelli compatibili installati", + "voiceLibrary.api.noInstalledModels": "Ancora nessuno. Installa uno dei modelli verificati dal server qui sopra prima di inviare questa richiesta.", + "voiceLibrary.api.curlExample": "Esempio cURL", + "voiceLibrary.api.copy": "Copia la richiesta", + "voiceLibrary.api.endpointNote": "Gli stessi campi model, input e voice funzionano anche con POST /tts.", + "voiceLibrary.actions.create": "Aggiungi una voce di sintesi", + "voiceLibrary.actions.createFirst": "Aggiungi la tua prima voce di sintesi", + "voiceLibrary.actions.retry": "Riprova", + "voiceLibrary.actions.clearFilters": "Rimuovi filtri", + "voiceLibrary.actions.useInTTS": "Usa nella sintesi vocale", + "voiceLibrary.actions.delete": "Elimina la voce", + "voiceLibrary.actions.installModel": "Installa un modello compatibile", + "voiceLibrary.actions.browseModels": "Sfoglia tutti i modelli", + "voiceLibrary.deleteDialog.title": "Eliminare questa voce?", + "voiceLibrary.deleteDialog.message": "“{{name}}” e il suo audio di riferimento saranno rimossi in modo definitivo. I modelli configurati con questa voce non cambiano.", + "voiceLibrary.deleteDialog.deleting": "Eliminazione…", + "voiceLibrary.toasts.deleted": "{{name}} eliminata", + "voiceLibrary.toasts.installStarted": "Installazione di {{name}}. L'avanzamento è in Operazioni.", + "voiceLibrary.toasts.installFailed": "Non è stato possibile avviare l'installazione: {{message}}", + "voiceLibrary.toasts.apiCopied": "Richiesta API copiata", + "voiceLibrary.toasts.copyFailed": "Non è stato possibile copiare la richiesta API", + "voiceCreate.title": "Aggiungi una voce di sintesi", + "voiceCreate.subtitle": "Una voce di sintesi è composta da una o più clip di riferimento pulite e dalle loro trascrizioni esatte. I modelli di sintesi vocale che accettano audio di riferimento possono parlare con essa.", + "voiceCreate.references.add": "Aggiungi un altro riferimento", + "voiceCreate.references.additional": "Riferimento {{number}}", + "voiceCreate.references.remove": "Rimuovi", + "voiceCreate.sections.reference.title": "Audio di riferimento", + "voiceCreate.sections.reference.body": "Registra nel browser o carica una clip esistente. La voce deve essere pulita, naturale e di un solo parlante.", + "voiceCreate.sections.details.title": "Identità e trascrizione", + "voiceCreate.sections.details.body": "Dai agli amministratori un nome riconoscibile e trascrivi la clip esattamente, con intercalari e punteggiatura.", + "voiceCreate.sections.consent.title": "Autorizzazione", + "voiceCreate.sections.consent.body": "La clonazione vocale può essere delicata. Conferma di avere il permesso prima di salvare questo riferimento.", + "voiceCreate.audio.label": "Riferimento vocale", + "voiceCreate.audio.normalizing": "Preparazione di un WAV PCM sicuro…", + "voiceCreate.audio.preview": "Riferimento normalizzato", + "voiceCreate.audio.tooLarge": "Questo file supera i 50 MiB.", + "voiceCreate.audio.durationError": "L'audio di riferimento deve durare tra 1 secondo e 2 minuti.", + "voiceCreate.audio.decodeError": "Questo browser non è riuscito a decodificare il file audio scelto.", + "voiceCreate.audio.qualityReady": "Durata consigliata", + "voiceCreate.audio.qualityHint": "Da 6 a 30 secondi di solito dà un clone più fedele", + "voiceCreate.fields.name": "Nome della voce", + "voiceCreate.fields.namePlaceholder": "es. Voce narrante di documentari", + "voiceCreate.fields.language": "Lingua (facoltativa)", + "voiceCreate.fields.languagePlaceholder": "es. it-IT", + "voiceCreate.fields.description": "Descrizione (facoltativa)", + "voiceCreate.fields.descriptionPlaceholder": "Tono, origine o uso previsto", + "voiceCreate.fields.transcript": "Trascrizione esatta", + "voiceCreate.fields.transcriptPlaceholder": "Scrivi ogni parola pronunciata nella clip di riferimento…", + "voiceCreate.fields.transcriptHint": "Segui la registrazione alla lettera. Non correggere la grammatica e non togliere pause e intercalari.", + "voiceCreate.consent.title": "Confermo che questa voce può essere clonata", + "voiceCreate.consent.body": "Ho il permesso del parlante, o un'altra base legittima, per conservare e usare questa registrazione per la sintesi vocale.", + "voiceCreate.readiness.title": "Stato del profilo", + "voiceCreate.readiness.body": "Una voce diventa disponibile nella sintesi vocale quando ogni elemento richiesto è completo.", + "voiceCreate.readiness.audio": "Riferimento PCM-WAV valido", + "voiceCreate.readiness.quality": "Durata di qualità tra 6 e 30 secondi", + "voiceCreate.readiness.name": "Nome della voce riconoscibile", + "voiceCreate.readiness.transcript": "Trascrizione di riferimento esatta", + "voiceCreate.readiness.consent": "Autorizzazione confermata", + "voiceCreate.privacy.title": "Privato per impostazione predefinita. ", + "voiceCreate.privacy.body": "L'audio di riferimento è conservato nella cartella dati di LocalAI con permessi di file ristretti e non è mai esposto come percorso nella cartella dei modelli.", + "voiceCreate.actions.back": "Torna alla libreria", + "voiceCreate.actions.cancel": "Annulla", + "voiceCreate.actions.save": "Salva la voce", + "voiceCreate.actions.saving": "Salvataggio…", + "voiceCreate.toasts.created": "{{name}} è pronta all'uso", + "clip.mic": "microfono", + "clip.camera": "fotocamera", + "clip.record": "Registra", + "clip.takePhoto": "Scatta una foto", + "clip.chooseAudio": "Scegli un file", + "clip.choosePhoto": "Scegli una foto", + "clip.readAloud": "Leggi a voce alta, a ritmo normale", + "clip.remove": "Rimuovi", + "clip.blocked": "L'accesso a «{{thing}}» è bloccato. Consentilo nelle impostazioni del sito del browser e riprova, oppure scegli un file.", + "clip.insecure": "Il browser apre «{{thing}}» solo su https o su localhost. Ora sei su {{origin}}. Apri questa pagina da localhost o aggiungi TLS, oppure scegli un file.", + "clip.unsupported": "Questo browser non può aprire «{{thing}}». Scegli invece un file.", + "clip.missing": "Su questo dispositivo non è stato trovato «{{thing}}». Scegli invece un file.", + "clip.failed": "Non è stato possibile aprire «{{thing}}». Scegli invece un file.", + "clip.allowed": "Microfono consentito.", + "clip.capture": "Acquisisci", + "clip.cancel": "Annulla", + "clip.recording": "Registrazione in corso, {{s}} s", + "clip.micReady": "Microfono pronto", + "clip.stop": "Ferma", + "clip.start": "Avvia la registrazione", + "clip.recorded": "Registrazione", + "clip.photographed": "Foto", + "clip.uploaded": "File caricato", + "clip.goodLength": "durata adatta", + "clip.short": "breve", + "clip.checkLevel": "controlla il livello", + "clip.preview": "Anteprima", + "clip.photoAlt": "La foto che hai scelto", + "same.text": "Distanza tra le due impronte ({{print}}): {{distance}}. Sotto {{cutoff}} si considera la stessa persona.", + "same.distance": "distanza", + "word.strong": "Corrispondenza forte", + "word.likely": "Corrispondenza probabile", + "word.close": "Caso al limite", + "word.near": "Appena oltre la soglia", + "scale.aria": "Scala delle distanze con la soglia a {{cutoff}}. Più basso significa più simile. {{dots}}", + "scale.cutoff": "soglia {{value}}", + "scale.axis": "distanza tra impronte ({{print}}), più bassa significa più simili", + "scale.oneDot": "un punto per ogni registrazione ({{person}})", + "scale.onePair": "un punto per la coppia", + "ranks.aria": "Persone più vicine, dalla più vicina", + "ranks.within": "entro la soglia", + "ranks.far": "troppo lontana", + "how.title": "Come leggere questo risultato.", + "how.body": "La distanza dice quanto sono lontane due impronte ({{print}}); 0 significa identiche. La soglia è la linea tra stessa persona e persone diverse. La parola Forte o Probabile dice quanto un risultato sta dentro la linea. Non è una probabilità.", + "stored.title": "Cosa viene conservato e dove", + "toast.enrolled": "{{name}} registrato", + "toast.enrolledBoth": "{{name}} registrato e voce di sintesi salvata", + "toast.tooBig": "La copia era troppo grande per la memoria del browser, quindi non è stata conservata.", + "toast.speechFailed": "Registrazione riuscita, ma la voce di sintesi non è stata salvata: {{message}}", + "toast.removing": "Rimozione di {{name}}…", + "toast.removed": "{{name}} rimosso", + "toast.alreadyGone": "{{name}} non c'era già più sul server.", + "error.retry": "Riprova", + "error.traces": "Apri le tracce", + "needed.badge": "Non attivo su questo server", + "needed.backend": "backend {{backend}}", + "needed.install": "Installa", + "needed.installing": "Installazione", + "needed.started": "Installazione di {{name}}. L'avanzamento è in Operazioni.", + "needed.loadFailed": "Non è stato possibile elencare i modelli.", + "needed.noneListed": "Nelle gallerie non c'è nessun modello corrispondente.", + "needed.browse": "Sfoglia tutti i modelli", + "needed.askAdmin": "Chiedi a un amministratore di installarne uno.", + "needed.whatTitle": "Cosa lo attiva", + "off.badge": "Non attivo per il tuo account", + "off.voice.title": "Il riconoscimento vocale è disattivato per il tuo account", + "off.voice.text": "Un amministratore non ti ha consentito di usare il riconoscimento vocale.", + "off.voice.what": "Un amministratore può attivare il permesso di riconoscimento vocale per il tuo utente in Utenti e chiavi.", + "recording.title": "Dai un nome ai parlanti di una registrazione", + "recording.text": "L'area di lavoro di diarizzazione trova chi ha parlato e quando in una registrazione. Da lì puoi ricordare un parlante, e quella voce entra nell'elenco dei parlanti qui.", + "recording.step1": "Apri l'area di lavoro, aggiungi una registrazione e spunta Prepara i parlanti da ricordare.", + "recording.step2": "Avvia la diarizzazione. Ogni parlante con abbastanza voce pulita ottiene un pulsante Dai un nome e ricorda.", + "recording.step3": "Dai un nome a un parlante e ricordalo. Quella voce compare poi in Parlanti, pronta per Chi è.", + "recording.open": "Apri l'area di lavoro di diarizzazione", + "recording.off": "La diarizzazione non è attiva per il tuo account.", + "recording.warning": "Le voci ricordate sono condivise su questo server e si perdono al riavvio. Non viene ricordato nulla in automatico." +} diff --git a/addons/voice/ui/src/i18n/voice.ko.json b/addons/voice/ui/src/i18n/voice.ko.json new file mode 100644 index 0000000000..18df3e581a --- /dev/null +++ b/addons/voice/ui/src/i18n/voice.ko.json @@ -0,0 +1,341 @@ +{ + "voice.adminOnly.title": "Administrators only", + "voice.adminOnly.text": "Saved voices can be managed by an administrator of this server. Ask one if you need a voice added or removed.", + "speech.title": "Speak text", + "speech.lede": "Turn text into speech with a voice model of this server.", + "speech.model": "Model", + "speech.voice": "Voice", + "speech.defaultVoice": "Default voice", + "speech.text": "텍스트", + "speech.instructions": "Instructions (optional)", + "speech.generate": "Generate speech", + "speech.result": "결과", + "speech.noModels": "No text-to-speech model is installed.", + "speech.noModelsLink": "Get one from the model gallery.", + "speech.voicePlaceholder": "음성 ID (선택 사항)", + "speech.textPlaceholder": "What should it say?", + "speech.noAccess": "Your account has no access to text-to-speech. The server will refuse the request.", + "voices.title": "음성", + "voices.lede": "Recognise who is speaking, and keep voices that text-to-speech can use.", + "voices.tabs": "Voices sections", + "voices.tab.recognition": "인식", + "voices.tab.library": "라이브러리", + "voices.tab.speech": "Text to speech", + "voices.tab.recording": "From a recording", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "진행 중", + "voice.speaker": "speaker", + "voice.print": "voiceprint", + "voice.whoTab": "Who is this", + "voice.whoLede": "Matches a clip against the people you enrolled.", + "voice.sameLede": "Two clips, one question. Nobody has to be enrolled.", + "voice.whoGo": "Identify speaker", + "voice.working": "Identifying", + "voice.whoNote": "Searches the speakers enrolled on this server. The clip you test with is not stored.", + "voice.sameNote": "The answer comes from one voiceprint per clip. Nothing is stored.", + "voice.workingText": "Comparing the clip with the voiceprints the server holds.", + "voice.probably": "Probably {{name}}", + "voice.matchText": "{{name}}'s voiceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "voice.nobody": "Nobody enrolled sounds like this", + "voice.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new speaker, or a clip that is too short or noisy.", + "voice.noneEnrolled": "Nobody is enrolled to compare with", + "voice.noneEnrolledText": "Enrol a speaker first, then identify a clip against them.", + "voice.noneReturned": "The server returned no speakers", + "voice.noneReturnedText": "It holds no voiceprints made with this model. If the server restarted, enrolled voices are gone: enrol them again.", + "voice.enrolThis": "Enrol this voice", + "voice.same": "Probably the same person", + "voice.different": "Probably different people", + "voice.knownTitle": "Known speakers", + "voice.enrol": "Enrol a speaker", + "voice.enrolTitle": "Enrol a speaker", + "voice.enrolIntro": "The server keeps a voiceprint and a name. It does not keep the recording.", + "voice.emptyTitle": "Nobody enrolled in this browser yet", + "voice.emptyText": "Enrol a speaker with a short recording, then test a clip against them.", + "voice.agreedHint": "I have their permission, or another lawful basis, to store a voiceprint of them.", + "voice.keepHint": "Lets you replay it and enrol again after a server restart. Off by default. It is stored in this browser only.", + "voice.neededTitle": "Voices need a speaker model", + "voice.neededText": "Voice recognition turns a recording into a voiceprint and compares voiceprints. It runs on a speaker-recognition model, and none is installed on this server.", + "voice.neededWhat": "Install a model that supports speaker recognition. Once one is installed, this page turns on by itself. Users also need the Voice recognition permission.", + "voice.moreTitle": "More tools", + "voice.moreHint": "guess a speaker's traits, get the raw voiceprint", + "voice.why.0": "One speaker is assumed. A clip with two people mixes both voices.", + "voice.why.1": "A different microphone or room changes how a voice sounds.", + "voice.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "voice.stored.0.where": "On the server", + "voice.stored.0.what": "A {{print}} (numbers that describe the voice, not the audio), a name and your labels. They are kept in memory, are shared by everyone who uses this server, and are lost when it restarts. The recording is not kept.", + "voice.stored.1.where": "In this browser", + "voice.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the recording only if you tick Keep a copy when enrolling.", + "voice.stored.2.where": "Clips you test with", + "voice.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "이름", + "known.added": "추가됨", + "known.actions": "작업", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "실행 취소", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "{{name}} 제거", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "enrol.close": "닫기", + "enrol.cancel": "취소", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Recording", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Recording", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "이름", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Exact transcript", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "tts.title": "텍스트 음성 변환", + "tts.labels.model": "모델", + "tts.labels.voice": "음성", + "tts.labels.voicePlaceholder": "음성 ID (선택 사항)", + "tts.labels.input": "텍스트", + "tts.labels.inputPlaceholder": "합성할 텍스트를 입력하세요...", + "tts.labels.instructions": "지침", + "tts.labels.instructionsPlaceholder": "예: 천천히 따뜻한 어조로 말하기", + "tts.labels.instructionsHint": "선택 사항인 말하기 지침입니다. 지원 여부는 백엔드에 따라 다릅니다.", + "tts.actions.generate": "생성", + "tts.actions.generating": "생성 중...", + "tts.empty": "생성된 오디오가 여기에 표시됩니다", + "tts.toasts.noText": "텍스트를 입력해 주세요", + "tts.toasts.noModel": "모델을 선택해 주세요", + "tts.toasts.generated": "Speech generated", + "tts.toasts.generateFailed": "생성에 실패했습니다", + "tts.voiceLibrary.cloningReady": "Voice cloning", + "tts.voiceLibrary.loading": "Loading saved personalities…", + "tts.voiceLibrary.modelDefault": "Use model default", + "tts.voiceLibrary.empty": "No saved personalities yet.", + "tts.voiceLibrary.create": "Create one", + "tts.voiceLibrary.manage": "Manage Personality Library", + "tts.voiceLibrary.namedVoiceHint": "This model does not accept reference-audio profiles. You can still enter a named speaker supported by its backend.", + "voiceLibrary.title": "Speech voices", + "voiceLibrary.subtitle": "A recording and its exact transcript. Text-to-speech models that accept reference audio can speak with it.", + "voiceLibrary.loadFailed": "Speech voices could not be loaded", + "voiceLibrary.loading": "Loading saved personalities…", + "voiceLibrary.listLabel": "Saved voice personalities", + "voiceLibrary.status.ready": "준비됨", + "voiceLibrary.summary.label": "Voice library status", + "voiceLibrary.summary.profiles": "saved personalities", + "voiceLibrary.summary.modelsReady_one": "{{count}} compatible model ready", + "voiceLibrary.summary.modelsReady_other": "{{count}} compatible models ready", + "voiceLibrary.summary.noModels": "No compatible model installed", + "voiceLibrary.search.label": "Search voice profiles", + "voiceLibrary.search.placeholder": "Search voices, languages, or transcripts", + "voiceLibrary.filters.language": "Filter by language", + "voiceLibrary.filters.allLanguages": "All languages", + "voiceLibrary.empty.title": "No speech voices yet", + "voiceLibrary.empty.body": "Record or upload a consented reference clip, add its exact transcript, and reuse the voice across compatible text-to-speech models.", + "voiceLibrary.noResults.title": "No voices match these filters", + "voiceLibrary.noResults.body": "Try a different search or show every language.", + "voiceLibrary.detail.eyebrow": "Reference recording and exact transcript", + "voiceLibrary.detail.close": "닫기", + "voiceLibrary.detail.emptyTitle": "Select a voice to inspect it", + "voiceLibrary.detail.emptyBody": "Reference audio, transcript, compatibility, and consent details will appear here.", + "voiceLibrary.detail.referenceAudio": "Reference audio", + "voiceLibrary.detail.transcript": "Reference transcript", + "voiceLibrary.metadata.language": "언어", + "voiceLibrary.metadata.languageUnknown": "Not specified", + "voiceLibrary.metadata.duration": "Duration", + "voiceLibrary.metadata.sampleRate": "Sample rate", + "voiceLibrary.metadata.created": "만듦", + "voiceLibrary.consent.label": "Permission", + "voiceLibrary.consent.confirmed": "Consent confirmed", + "voiceLibrary.consent.confirmedAt": "Confirmed {{date}}", + "voiceLibrary.modelSetup.title": "Install a voice-cloning model", + "voiceLibrary.modelSetup.body": "This voice is ready, but it needs a model that accepts reference audio. Choose one verified by this server.", + "voiceLibrary.modelSetup.loading": "Finding compatible models in your galleries…", + "voiceLibrary.modelSetup.backendUnknown": "Backend selected during install", + "voiceLibrary.modelSetup.install": "설치", + "voiceLibrary.modelSetup.installing": "설치 중…", + "voiceLibrary.modelSetup.loadFailed": "Compatible recommendations could not be loaded.", + "voiceLibrary.modelSetup.noneAvailable": "No compatible uninstalled model is available in the configured galleries.", + "voiceLibrary.modelSetup.capabilityNote": "Recommendations come from model capability metadata, not a browser-side model list.", + "voiceLibrary.api.title": "API usage and compatible models", + "voiceLibrary.api.summary": "Use this voice outside the browser or check model support", + "voiceLibrary.api.body": "Send the profile’s stable voice URI with each speech request. LocalAI resolves the private reference audio and transcript at generation time; the settings of your models are not changed.", + "voiceLibrary.api.compatibleModels": "Compatible installed models", + "voiceLibrary.api.noInstalledModels": "None yet. Install one of the server-verified models above before sending this request.", + "voiceLibrary.api.curlExample": "cURL example", + "voiceLibrary.api.copy": "Copy request", + "voiceLibrary.api.endpointNote": "The same model, input, and voice fields also work with POST /tts.", + "voiceLibrary.actions.create": "Add a speech voice", + "voiceLibrary.actions.createFirst": "Add your first speech voice", + "voiceLibrary.actions.retry": "다시 시도", + "voiceLibrary.actions.clearFilters": "필터 지우기", + "voiceLibrary.actions.useInTTS": "Use in Text to Speech", + "voiceLibrary.actions.delete": "Delete voice", + "voiceLibrary.actions.installModel": "Install a compatible model", + "voiceLibrary.actions.browseModels": "Browse all models", + "voiceLibrary.deleteDialog.title": "Delete this voice?", + "voiceLibrary.deleteDialog.message": "“{{name}}” and its reference audio will be permanently removed. Models set up with this voice are not changed.", + "voiceLibrary.deleteDialog.deleting": "Deleting…", + "voiceLibrary.toasts.deleted": "Deleted {{name}}", + "voiceLibrary.toasts.installStarted": "Installing {{name}}. Progress is available in Operations.", + "voiceLibrary.toasts.installFailed": "Could not start installation: {{message}}", + "voiceLibrary.toasts.apiCopied": "API request copied", + "voiceLibrary.toasts.copyFailed": "Could not copy the API request", + "voiceCreate.title": "Add a speech voice", + "voiceCreate.subtitle": "A speech voice is one or more clean reference clips and their exact transcripts. Text-to-speech models that accept reference audio can speak with it.", + "voiceCreate.references.add": "Add another reference", + "voiceCreate.references.additional": "Reference {{number}}", + "voiceCreate.references.remove": "제거", + "voiceCreate.sections.reference.title": "Reference audio", + "voiceCreate.sections.reference.body": "Record in the browser or upload an existing clip. Speech should be clean, natural, and from one speaker.", + "voiceCreate.sections.details.title": "Identity and transcript", + "voiceCreate.sections.details.body": "Give admins a recognizable name and transcribe the clip exactly, including fillers and punctuation.", + "voiceCreate.sections.consent.title": "Authorization", + "voiceCreate.sections.consent.body": "Voice cloning can be sensitive. Confirm permission before this reference is saved.", + "voiceCreate.audio.label": "Voice reference", + "voiceCreate.audio.normalizing": "Preparing a secure PCM WAV…", + "voiceCreate.audio.preview": "Normalized reference", + "voiceCreate.audio.tooLarge": "This file is larger than 50 MiB.", + "voiceCreate.audio.durationError": "Reference audio must be between 1 second and 2 minutes.", + "voiceCreate.audio.decodeError": "This browser could not decode the selected audio file.", + "voiceCreate.audio.qualityReady": "Recommended length", + "voiceCreate.audio.qualityHint": "6–30 seconds usually produces a stronger clone", + "voiceCreate.fields.name": "Voice name", + "voiceCreate.fields.namePlaceholder": "e.g. Documentary narrator", + "voiceCreate.fields.language": "Language (optional)", + "voiceCreate.fields.languagePlaceholder": "e.g. en-US", + "voiceCreate.fields.description": "Description (optional)", + "voiceCreate.fields.descriptionPlaceholder": "Tone, source, or intended use", + "voiceCreate.fields.transcript": "Exact transcript", + "voiceCreate.fields.transcriptPlaceholder": "Type every word spoken in the reference clip…", + "voiceCreate.fields.transcriptHint": "Match the recording exactly. Do not rewrite grammar or remove pauses and fillers.", + "voiceCreate.consent.title": "I confirm that this voice may be cloned", + "voiceCreate.consent.body": "I have the speaker’s permission or another lawful basis to store and use this recording for synthesized speech.", + "voiceCreate.readiness.title": "Profile readiness", + "voiceCreate.readiness.body": "A voice becomes available in Text to Speech when every required item is complete.", + "voiceCreate.readiness.audio": "Valid PCM-WAV reference", + "voiceCreate.readiness.quality": "6–30 second quality window", + "voiceCreate.readiness.name": "Recognizable voice name", + "voiceCreate.readiness.transcript": "Exact reference transcript", + "voiceCreate.readiness.consent": "Authorization confirmed", + "voiceCreate.privacy.title": "Private by default. ", + "voiceCreate.privacy.body": "Reference audio is stored in LocalAI’s data directory with restricted file permissions and is never exposed as a model-folder path.", + "voiceCreate.actions.back": "Back to library", + "voiceCreate.actions.cancel": "취소", + "voiceCreate.actions.save": "Save voice", + "voiceCreate.actions.saving": "Saving…", + "voiceCreate.toasts.created": "{{name}} is ready to use", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Record", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "제거", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "취소", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "중지", + "clip.start": "Start recording", + "clip.recorded": "Recording", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "미리보기", + "clip.photoAlt": "The photo you chose", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "stored.title": "What is stored, and where", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "다시 시도", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "설치", + "needed.installing": "설치 중", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Browse all models", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "recording.title": "Name the speakers in a recording", + "recording.text": "The diarization workspace finds who spoke when in a recording. From there you can remember a speaker, and that voice joins the Speakers list here.", + "recording.step1": "Open the workspace, add a recording and tick Prepare speakers to remember.", + "recording.step2": "Run diarization. Each speaker with enough clean speech gets a Name and remember button.", + "recording.step3": "Name a speaker and remember it. That voice then appears under Speakers, ready for Who is this.", + "recording.open": "Open the diarization workspace", + "recording.off": "Diarization is not turned on for your account.", + "recording.warning": "Remembered voices are shared on this server and are lost when it restarts. Nothing is remembered automatically." +} diff --git a/addons/voice/ui/src/i18n/voice.pt-BR.json b/addons/voice/ui/src/i18n/voice.pt-BR.json new file mode 100644 index 0000000000..fc5aa1338d --- /dev/null +++ b/addons/voice/ui/src/i18n/voice.pt-BR.json @@ -0,0 +1,341 @@ +{ + "voice.adminOnly.title": "Administrators only", + "voice.adminOnly.text": "Saved voices can be managed by an administrator of this server. Ask one if you need a voice added or removed.", + "speech.title": "Speak text", + "speech.lede": "Turn text into speech with a voice model of this server.", + "speech.model": "Model", + "speech.voice": "Voz", + "speech.defaultVoice": "Default voice", + "speech.text": "Texto", + "speech.instructions": "Instructions (optional)", + "speech.generate": "Generate speech", + "speech.result": "Resultado", + "speech.noModels": "No text-to-speech model is installed.", + "speech.noModelsLink": "Get one from the model gallery.", + "speech.voicePlaceholder": "Falante opcional ou ID de voz", + "speech.textPlaceholder": "What should it say?", + "speech.noAccess": "Your account has no access to text-to-speech. The server will refuse the request.", + "voices.title": "Vozes", + "voices.lede": "Recognise who is speaking, and keep voices that text-to-speech can use.", + "voices.tabs": "Voices sections", + "voices.tab.recognition": "Reconhecimento", + "voices.tab.library": "Biblioteca", + "voices.tab.speech": "Text to speech", + "voices.tab.recording": "From a recording", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "Em andamento", + "voice.speaker": "speaker", + "voice.print": "voiceprint", + "voice.whoTab": "Who is this", + "voice.whoLede": "Matches a clip against the people you enrolled.", + "voice.sameLede": "Two clips, one question. Nobody has to be enrolled.", + "voice.whoGo": "Identify speaker", + "voice.working": "Identifying", + "voice.whoNote": "Searches the speakers enrolled on this server. The clip you test with is not stored.", + "voice.sameNote": "The answer comes from one voiceprint per clip. Nothing is stored.", + "voice.workingText": "Comparing the clip with the voiceprints the server holds.", + "voice.probably": "Probably {{name}}", + "voice.matchText": "{{name}}'s voiceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "voice.nobody": "Nobody enrolled sounds like this", + "voice.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new speaker, or a clip that is too short or noisy.", + "voice.noneEnrolled": "Nobody is enrolled to compare with", + "voice.noneEnrolledText": "Enrol a speaker first, then identify a clip against them.", + "voice.noneReturned": "The server returned no speakers", + "voice.noneReturnedText": "It holds no voiceprints made with this model. If the server restarted, enrolled voices are gone: enrol them again.", + "voice.enrolThis": "Enrol this voice", + "voice.same": "Probably the same person", + "voice.different": "Probably different people", + "voice.knownTitle": "Known speakers", + "voice.enrol": "Enrol a speaker", + "voice.enrolTitle": "Enrol a speaker", + "voice.enrolIntro": "The server keeps a voiceprint and a name. It does not keep the recording.", + "voice.emptyTitle": "Nobody enrolled in this browser yet", + "voice.emptyText": "Enrol a speaker with a short recording, then test a clip against them.", + "voice.agreedHint": "I have their permission, or another lawful basis, to store a voiceprint of them.", + "voice.keepHint": "Lets you replay it and enrol again after a server restart. Off by default. It is stored in this browser only.", + "voice.neededTitle": "Voices need a speaker model", + "voice.neededText": "Voice recognition turns a recording into a voiceprint and compares voiceprints. It runs on a speaker-recognition model, and none is installed on this server.", + "voice.neededWhat": "Install a model that supports speaker recognition. Once one is installed, this page turns on by itself. Users also need the Voice recognition permission.", + "voice.moreTitle": "More tools", + "voice.moreHint": "guess a speaker's traits, get the raw voiceprint", + "voice.why.0": "One speaker is assumed. A clip with two people mixes both voices.", + "voice.why.1": "A different microphone or room changes how a voice sounds.", + "voice.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "voice.stored.0.where": "On the server", + "voice.stored.0.what": "A {{print}} (numbers that describe the voice, not the audio), a name and your labels. They are kept in memory, are shared by everyone who uses this server, and are lost when it restarts. The recording is not kept.", + "voice.stored.1.where": "In this browser", + "voice.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the recording only if you tick Keep a copy when enrolling.", + "voice.stored.2.where": "Clips you test with", + "voice.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "Nome", + "known.added": "Adicionado", + "known.actions": "Ações", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "Desfazer", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "Remover {{name}}", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "enrol.close": "Fechar", + "enrol.cancel": "Cancelar", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Recording", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Recording", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "Nome", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Transcrição exata", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "tts.title": "Texto para Fala", + "tts.labels.model": "Modelo", + "tts.labels.voice": "Voz", + "tts.labels.voicePlaceholder": "Falante opcional ou ID de voz", + "tts.labels.input": "Texto", + "tts.labels.inputPlaceholder": "Digite o texto para sintetizar...", + "tts.labels.instructions": "Instruções", + "tts.labels.instructionsPlaceholder": "Por exemplo: Fale devagar e com calor", + "tts.labels.instructionsHint": "Orientações opcionais de interpretação. O suporte depende do backend.", + "tts.actions.generate": "Gerar", + "tts.actions.generating": "Gerando...", + "tts.empty": "O áudio gerado aparecerá aqui", + "tts.toasts.noText": "Digite o texto", + "tts.toasts.noModel": "Selecione um modelo", + "tts.toasts.generated": "Fala gerada", + "tts.toasts.generateFailed": "Falha na geração", + "tts.voiceLibrary.cloningReady": "Clonagem de voz", + "tts.voiceLibrary.loading": "Carregando vozes salvas…", + "tts.voiceLibrary.modelDefault": "Usar padrão do modelo", + "tts.voiceLibrary.empty": "Nenhuma voz salva ainda.", + "tts.voiceLibrary.create": "Criar uma", + "tts.voiceLibrary.manage": "Gerenciar Biblioteca de Vozes", + "tts.voiceLibrary.namedVoiceHint": "Este modelo não aceita perfis de áudio de referência. Você ainda pode digitar um falante nomeado suportado pelo seu backend.", + "voiceLibrary.title": "Biblioteca de Vozes", + "voiceLibrary.subtitle": "Crie e gerencie vozes de referência reutilizáveis para cada modelo instalado que suporta clonagem de voz.", + "voiceLibrary.loadFailed": "Speech voices could not be loaded", + "voiceLibrary.loading": "Carregando vozes salvas…", + "voiceLibrary.listLabel": "Perfis de voz salvos", + "voiceLibrary.status.ready": "Pronto", + "voiceLibrary.summary.label": "Status da biblioteca de vozes", + "voiceLibrary.summary.profiles": "vozes salvas", + "voiceLibrary.summary.modelsReady_one": "{{count}} modelo compatível pronto", + "voiceLibrary.summary.modelsReady_other": "{{count}} modelos compatíveis prontos", + "voiceLibrary.summary.noModels": "Nenhum modelo compatível instalado", + "voiceLibrary.search.label": "Buscar perfis de voz", + "voiceLibrary.search.placeholder": "Buscar vozes, idiomas ou transcrições", + "voiceLibrary.filters.language": "Filtrar por idioma", + "voiceLibrary.filters.allLanguages": "Todos os idiomas", + "voiceLibrary.empty.title": "Sua biblioteca de vozes está vazia", + "voiceLibrary.empty.body": "Grave ou envie um clipe de referência autorizado, adicione a transcrição exata e reuse-o nos modelos de TTS compatíveis.", + "voiceLibrary.noResults.title": "Nenhuma voz corresponde a estes filtros", + "voiceLibrary.noResults.body": "Tente uma busca diferente ou mostre todos os idiomas.", + "voiceLibrary.detail.eyebrow": "Voz selecionada", + "voiceLibrary.detail.close": "Fechar", + "voiceLibrary.detail.emptyTitle": "Selecione uma voz para inspecioná-la", + "voiceLibrary.detail.emptyBody": "Áudio de referência, transcrição, compatibilidade e detalhes de consentimento aparecerão aqui.", + "voiceLibrary.detail.referenceAudio": "Áudio de referência", + "voiceLibrary.detail.transcript": "Transcrição de referência", + "voiceLibrary.metadata.language": "Idioma", + "voiceLibrary.metadata.languageUnknown": "Não especificado", + "voiceLibrary.metadata.duration": "Duração", + "voiceLibrary.metadata.sampleRate": "Taxa de amostragem", + "voiceLibrary.metadata.created": "Criado em", + "voiceLibrary.consent.label": "Permission", + "voiceLibrary.consent.confirmed": "Consentimento confirmado", + "voiceLibrary.consent.confirmedAt": "Confirmado em {{date}}", + "voiceLibrary.modelSetup.title": "Instalar um modelo de clonagem de voz", + "voiceLibrary.modelSetup.body": "Esta voz está pronta, mas precisa de um modelo que aceite áudio de referência. Escolha um verificado por este servidor.", + "voiceLibrary.modelSetup.loading": "Encontrando modelos compatíveis em suas galerias…", + "voiceLibrary.modelSetup.backendUnknown": "Backend selecionado durante a instalação", + "voiceLibrary.modelSetup.install": "Instalar", + "voiceLibrary.modelSetup.installing": "Instalando…", + "voiceLibrary.modelSetup.loadFailed": "Recomendações compatíveis não puderam ser carregadas.", + "voiceLibrary.modelSetup.noneAvailable": "Nenhum modelo compatível não instalado está disponível nas galerias configuradas.", + "voiceLibrary.modelSetup.capabilityNote": "As recomendações vêm dos metadados de capacidade do modelo, não de uma lista de modelos do lado do navegador.", + "voiceLibrary.api.title": "Uso da API e modelos compatíveis", + "voiceLibrary.api.summary": "Use esta voz fora do navegador ou verifique o suporte do modelo", + "voiceLibrary.api.body": "Envie a URI de voz estável do perfil com cada requisição de fala. O LocalAI resolve o áudio de referência privado e a transcrição no momento da geração; as configurações dos seus modelos não são alteradas.", + "voiceLibrary.api.compatibleModels": "Modelos compatíveis instalados", + "voiceLibrary.api.noInstalledModels": "Nenhum ainda. Instale um dos modelos verificados pelo servidor acima antes de enviar esta requisição.", + "voiceLibrary.api.curlExample": "Exemplo com cURL", + "voiceLibrary.api.copy": "Copiar requisição", + "voiceLibrary.api.endpointNote": "Os mesmos campos de modelo, entrada e voz também funcionam com POST /tts.", + "voiceLibrary.actions.create": "Criar voz", + "voiceLibrary.actions.createFirst": "Crie sua primeira voz", + "voiceLibrary.actions.retry": "Tentar novamente", + "voiceLibrary.actions.clearFilters": "Limpar filtros", + "voiceLibrary.actions.useInTTS": "Usar em Texto para Fala", + "voiceLibrary.actions.delete": "Excluir voz", + "voiceLibrary.actions.installModel": "Instalar um modelo compatível", + "voiceLibrary.actions.browseModels": "Explorar todos os modelos", + "voiceLibrary.deleteDialog.title": "Excluir esta voz?", + "voiceLibrary.deleteDialog.message": "“{{name}}” e seu áudio de referência serão removidos permanentemente. Os modelos configurados com esta voz não são alterados.", + "voiceLibrary.deleteDialog.deleting": "Excluindo…", + "voiceLibrary.toasts.deleted": "Voz {{name}} excluída", + "voiceLibrary.toasts.installStarted": "Instalando {{name}}. O progresso está disponível em Operações.", + "voiceLibrary.toasts.installFailed": "Não foi possível iniciar a instalação: {{message}}", + "voiceLibrary.toasts.apiCopied": "Requisição de API copiada", + "voiceLibrary.toasts.copyFailed": "Não foi possível copiar a requisição de API", + "voiceCreate.title": "Criar uma voz reutilizável", + "voiceCreate.subtitle": "Adicione um clipe de referência limpo e sua transcrição exata. O LocalAI cuida dos parâmetros de clonagem específicos do backend no momento da geração.", + "voiceCreate.references.add": "Add another reference", + "voiceCreate.references.additional": "Reference {{number}}", + "voiceCreate.references.remove": "Remover", + "voiceCreate.sections.reference.title": "Áudio de referência", + "voiceCreate.sections.reference.body": "Grave no navegador ou envie um clipe existente. A fala deve ser limpa, natural e de um único falante.", + "voiceCreate.sections.details.title": "Identidade e transcrição", + "voiceCreate.sections.details.body": "Dê aos administradores um nome reconhecível e transcreva o clipe exatamente, incluindo hesitações e pontuação.", + "voiceCreate.sections.consent.title": "Autorização", + "voiceCreate.sections.consent.body": "A clonagem de voz pode ser sensível. Confirme a permissão antes que esta referência seja salva.", + "voiceCreate.audio.label": "Referência de voz", + "voiceCreate.audio.normalizing": "Preparando um PCM WAV seguro…", + "voiceCreate.audio.preview": "Referência normalizada", + "voiceCreate.audio.tooLarge": "This file is larger than 50 MiB.", + "voiceCreate.audio.durationError": "O áudio de referência deve ter entre 1 segundo e 2 minutos.", + "voiceCreate.audio.decodeError": "Este navegador não conseguiu decodificar o arquivo de áudio selecionado.", + "voiceCreate.audio.qualityReady": "Duração recomendada", + "voiceCreate.audio.qualityHint": "6–30 segundos geralmente produzem um clone mais forte", + "voiceCreate.fields.name": "Nome da voz", + "voiceCreate.fields.namePlaceholder": "ex.: Narrador de documentário", + "voiceCreate.fields.language": "Idioma (opcional)", + "voiceCreate.fields.languagePlaceholder": "ex.: pt-BR", + "voiceCreate.fields.description": "Descrição (opcional)", + "voiceCreate.fields.descriptionPlaceholder": "Tom, origem ou uso pretendido", + "voiceCreate.fields.transcript": "Transcrição exata", + "voiceCreate.fields.transcriptPlaceholder": "Digite cada palavra falada no clipe de referência…", + "voiceCreate.fields.transcriptHint": "Corresponda à gravação exatamente. Não reescreva a gramática nem remova pausas e hesitações.", + "voiceCreate.consent.title": "Confirmo que esta voz pode ser clonada", + "voiceCreate.consent.body": "Tenho a permissão do falante ou outra base legal para armazenar e usar esta gravação para fala sintetizada.", + "voiceCreate.readiness.title": "Prontidão do perfil", + "voiceCreate.readiness.body": "Uma voz fica disponível em Texto para Fala quando todos os itens obrigatórios estão completos.", + "voiceCreate.readiness.audio": "Referência PCM-WAV válida", + "voiceCreate.readiness.quality": "Janela de qualidade de 6–30 segundos", + "voiceCreate.readiness.name": "Nome de voz reconhecível", + "voiceCreate.readiness.transcript": "Transcrição de referência exata", + "voiceCreate.readiness.consent": "Autorização confirmada", + "voiceCreate.privacy.title": "Privada por padrão. ", + "voiceCreate.privacy.body": "O áudio de referência é armazenado no diretório de dados do LocalAI com permissões de arquivo restritas e nunca é exposto como um caminho de pasta de modelo.", + "voiceCreate.actions.back": "Voltar para a biblioteca", + "voiceCreate.actions.cancel": "Cancelar", + "voiceCreate.actions.save": "Salvar voz", + "voiceCreate.actions.saving": "Salvando…", + "voiceCreate.toasts.created": "{{name}} está pronta para uso", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Gravar", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "Remover", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "Cancelar", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "Parar", + "clip.start": "Iniciar gravação", + "clip.recorded": "Recording", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "Pré-visualização", + "clip.photoAlt": "The photo you chose", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "stored.title": "What is stored, and where", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "Tentar novamente", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "Instalar", + "needed.installing": "Instalando", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Explorar todos os modelos", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "recording.title": "Name the speakers in a recording", + "recording.text": "The diarization workspace finds who spoke when in a recording. From there you can remember a speaker, and that voice joins the Speakers list here.", + "recording.step1": "Open the workspace, add a recording and tick Prepare speakers to remember.", + "recording.step2": "Run diarization. Each speaker with enough clean speech gets a Name and remember button.", + "recording.step3": "Name a speaker and remember it. That voice then appears under Speakers, ready for Who is this.", + "recording.open": "Open the diarization workspace", + "recording.off": "Diarization is not turned on for your account.", + "recording.warning": "Remembered voices are shared on this server and are lost when it restarts. Nothing is remembered automatically." +} diff --git a/addons/voice/ui/src/i18n/voice.zh-CN.json b/addons/voice/ui/src/i18n/voice.zh-CN.json new file mode 100644 index 0000000000..5ad6a95a7a --- /dev/null +++ b/addons/voice/ui/src/i18n/voice.zh-CN.json @@ -0,0 +1,341 @@ +{ + "voice.adminOnly.title": "Administrators only", + "voice.adminOnly.text": "Saved voices can be managed by an administrator of this server. Ask one if you need a voice added or removed.", + "speech.title": "Speak text", + "speech.lede": "Turn text into speech with a voice model of this server.", + "speech.model": "Model", + "speech.voice": "Voice", + "speech.defaultVoice": "Default voice", + "speech.text": "文本", + "speech.instructions": "Instructions (optional)", + "speech.generate": "Generate speech", + "speech.result": "结果", + "speech.noModels": "No text-to-speech model is installed.", + "speech.noModelsLink": "Get one from the model gallery.", + "speech.voicePlaceholder": "可选的声音 ID", + "speech.textPlaceholder": "What should it say?", + "speech.noAccess": "Your account has no access to text-to-speech. The server will refuse the request.", + "voices.title": "声音", + "voices.lede": "Recognise who is speaking, and keep voices that text-to-speech can use.", + "voices.tabs": "Voices sections", + "voices.tab.recognition": "识别", + "voices.tab.library": "库", + "voices.tab.speech": "Text to speech", + "voices.tab.recording": "From a recording", + "tool.aria": "Recognition tool", + "tool.sameTab": "Same person?", + "tool.clip": "Clip", + "tool.clipA": "Clip A", + "tool.clipB": "Clip B", + "tool.photo": "Photo", + "tool.photoA": "Photo A", + "tool.photoB": "Photo B", + "tool.compare": "Compare", + "tool.comparing": "Comparing", + "tool.workingAria": "进行中", + "voice.speaker": "speaker", + "voice.print": "voiceprint", + "voice.whoTab": "Who is this", + "voice.whoLede": "Matches a clip against the people you enrolled.", + "voice.sameLede": "Two clips, one question. Nobody has to be enrolled.", + "voice.whoGo": "Identify speaker", + "voice.working": "Identifying", + "voice.whoNote": "Searches the speakers enrolled on this server. The clip you test with is not stored.", + "voice.sameNote": "The answer comes from one voiceprint per clip. Nothing is stored.", + "voice.workingText": "Comparing the clip with the voiceprints the server holds.", + "voice.probably": "Probably {{name}}", + "voice.matchText": "{{name}}'s voiceprint is the closest of {{count}} compared, {{distance}} away. Anything under {{cutoff}} counts as the same person.", + "voice.nobody": "Nobody enrolled sounds like this", + "voice.missText": "The closest was {{name}} at {{distance}}, past the cut-off of {{cutoff}}. That usually means a new speaker, or a clip that is too short or noisy.", + "voice.noneEnrolled": "Nobody is enrolled to compare with", + "voice.noneEnrolledText": "Enrol a speaker first, then identify a clip against them.", + "voice.noneReturned": "The server returned no speakers", + "voice.noneReturnedText": "It holds no voiceprints made with this model. If the server restarted, enrolled voices are gone: enrol them again.", + "voice.enrolThis": "Enrol this voice", + "voice.same": "Probably the same person", + "voice.different": "Probably different people", + "voice.knownTitle": "Known speakers", + "voice.enrol": "Enrol a speaker", + "voice.enrolTitle": "Enrol a speaker", + "voice.enrolIntro": "The server keeps a voiceprint and a name. It does not keep the recording.", + "voice.emptyTitle": "Nobody enrolled in this browser yet", + "voice.emptyText": "Enrol a speaker with a short recording, then test a clip against them.", + "voice.agreedHint": "I have their permission, or another lawful basis, to store a voiceprint of them.", + "voice.keepHint": "Lets you replay it and enrol again after a server restart. Off by default. It is stored in this browser only.", + "voice.neededTitle": "Voices need a speaker model", + "voice.neededText": "Voice recognition turns a recording into a voiceprint and compares voiceprints. It runs on a speaker-recognition model, and none is installed on this server.", + "voice.neededWhat": "Install a model that supports speaker recognition. Once one is installed, this page turns on by itself. Users also need the Voice recognition permission.", + "voice.moreTitle": "More tools", + "voice.moreHint": "guess a speaker's traits, get the raw voiceprint", + "voice.why.0": "One speaker is assumed. A clip with two people mixes both voices.", + "voice.why.1": "A different microphone or room changes how a voice sounds.", + "voice.why.2": "This is a similarity score, not a security check. Do not use it to unlock anything.", + "voice.stored.0.where": "On the server", + "voice.stored.0.what": "A {{print}} (numbers that describe the voice, not the audio), a name and your labels. They are kept in memory, are shared by everyone who uses this server, and are lost when it restarts. The recording is not kept.", + "voice.stored.1.where": "In this browser", + "voice.stored.1.what": "The name, labels and time of each person you enrolled. A copy of the recording only if you tick Keep a copy when enrolling.", + "voice.stored.2.where": "Clips you test with", + "voice.stored.2.what": "They go to the model on this server to be compared and are not kept.", + "cutoff.label": "Cut-off", + "cutoff.default": "model default", + "cutoff.reset": "Reset to {{value}}", + "cutoff.stricter": "Stricter", + "cutoff.looser": "Looser", + "cutoff.explain": "Stricter means fewer wrong matches and more misses. The answer above updates as you move it. The server is not asked again.", + "why.title": "Why this might be wrong", + "why.short": "The clip is short: {{s}} s. Three seconds or more of speech works better.", + "known.caption": "People saved in this browser, with their {{noun}} state", + "known.name": "名称", + "known.added": "添加时间", + "known.actions": "操作", + "known.best": "best match", + "known.notOnServer": "not on the server", + "known.notInBrowser": "not in this browser's list", + "known.removing": "removing", + "known.goneText": "The last search did not return this person. The server may have restarted, so the {{print}} is gone, or it was enrolled with another model.", + "known.extraText": "The server returned this person but this browser has no record of them.", + "known.noLabels": "no labels", + "known.undo": "撤销", + "known.reenrolSaved": "Re-enrol from saved copy", + "known.enrolAgain": "Enrol again", + "known.play": "Play the saved sample of {{name}}", + "known.forget": "移除 {{name}}", + "known.note": "This list is kept in this browser. The server has no list call, so it cannot be checked until you run a search.", + "known.noteSearched": "This list is kept in this browser. The last search checked it against the server; a person it did not return is marked.", + "enrol.close": "关闭", + "enrol.cancel": "取消", + "enrol.submit": "Enrol", + "enrol.working": "Enrolling", + "enrol.ready": "Ready to enrol.", + "enrol.stillNeeded": "Still needed: {{list}}.", + "enrol.needModel": "a model", + "enrol.needRecording": "a recording", + "enrol.needPhoto": "a photo", + "enrol.needName": "a name", + "enrol.needTranscript": "a transcript", + "enrol.needPermission": "permission", + "enrol.recordingStep": "Recording", + "enrol.photoStep": "Photo", + "enrol.recordingLabel": "Recording", + "enrol.photoLabel": "Photo", + "enrol.whoStep": "Who is it", + "enrol.name": "名称", + "enrol.namePlaceholder": "e.g. Marta Rossi", + "enrol.labels": "Labels", + "enrol.labelsHint": "optional, one per line as key: value", + "enrol.asSpeech": "Also keep it as a speech voice", + "enrol.asSpeechHint": "Text-to-speech can then speak in this voice. Needs the exact transcript.", + "enrol.transcript": "Exact transcript", + "enrol.permissionStep": "Permission", + "enrol.agreed": "This person agreed", + "enrol.keep": "Keep a copy in this browser", + "tts.title": "文字转语音", + "tts.labels.model": "模型", + "tts.labels.voice": "声音", + "tts.labels.voicePlaceholder": "可选的声音 ID", + "tts.labels.input": "文本", + "tts.labels.inputPlaceholder": "输入要合成的文本...", + "tts.labels.instructions": "朗读指令", + "tts.labels.instructionsPlaceholder": "例如:缓慢、温暖地朗读", + "tts.labels.instructionsHint": "可选的表达方式指导。是否支持取决于后端。", + "tts.actions.generate": "生成音频", + "tts.actions.generating": "生成中...", + "tts.empty": "生成的音频将显示在此处", + "tts.toasts.noText": "请输入文本", + "tts.toasts.noModel": "请选择一个模型", + "tts.toasts.generated": "Speech generated", + "tts.toasts.generateFailed": "生成失败", + "tts.voiceLibrary.cloningReady": "Voice cloning", + "tts.voiceLibrary.loading": "Loading saved personalities…", + "tts.voiceLibrary.modelDefault": "Use model default", + "tts.voiceLibrary.empty": "No saved personalities yet.", + "tts.voiceLibrary.create": "Create one", + "tts.voiceLibrary.manage": "Manage Personality Library", + "tts.voiceLibrary.namedVoiceHint": "This model does not accept reference-audio profiles. You can still enter a named speaker supported by its backend.", + "voiceLibrary.title": "Speech voices", + "voiceLibrary.subtitle": "A recording and its exact transcript. Text-to-speech models that accept reference audio can speak with it.", + "voiceLibrary.loadFailed": "Speech voices could not be loaded", + "voiceLibrary.loading": "Loading saved personalities…", + "voiceLibrary.listLabel": "Saved voice personalities", + "voiceLibrary.status.ready": "已就绪", + "voiceLibrary.summary.label": "Voice library status", + "voiceLibrary.summary.profiles": "saved personalities", + "voiceLibrary.summary.modelsReady_one": "{{count}} compatible model ready", + "voiceLibrary.summary.modelsReady_other": "{{count}} compatible models ready", + "voiceLibrary.summary.noModels": "No compatible model installed", + "voiceLibrary.search.label": "Search voice profiles", + "voiceLibrary.search.placeholder": "Search voices, languages, or transcripts", + "voiceLibrary.filters.language": "Filter by language", + "voiceLibrary.filters.allLanguages": "All languages", + "voiceLibrary.empty.title": "No speech voices yet", + "voiceLibrary.empty.body": "Record or upload a consented reference clip, add its exact transcript, and reuse the voice across compatible text-to-speech models.", + "voiceLibrary.noResults.title": "No voices match these filters", + "voiceLibrary.noResults.body": "Try a different search or show every language.", + "voiceLibrary.detail.eyebrow": "Reference recording and exact transcript", + "voiceLibrary.detail.close": "关闭", + "voiceLibrary.detail.emptyTitle": "Select a voice to inspect it", + "voiceLibrary.detail.emptyBody": "Reference audio, transcript, compatibility, and consent details will appear here.", + "voiceLibrary.detail.referenceAudio": "Reference audio", + "voiceLibrary.detail.transcript": "Reference transcript", + "voiceLibrary.metadata.language": "语言", + "voiceLibrary.metadata.languageUnknown": "Not specified", + "voiceLibrary.metadata.duration": "Duration", + "voiceLibrary.metadata.sampleRate": "Sample rate", + "voiceLibrary.metadata.created": "已创建", + "voiceLibrary.consent.label": "Permission", + "voiceLibrary.consent.confirmed": "Consent confirmed", + "voiceLibrary.consent.confirmedAt": "Confirmed {{date}}", + "voiceLibrary.modelSetup.title": "Install a voice-cloning model", + "voiceLibrary.modelSetup.body": "This voice is ready, but it needs a model that accepts reference audio. Choose one verified by this server.", + "voiceLibrary.modelSetup.loading": "Finding compatible models in your galleries…", + "voiceLibrary.modelSetup.backendUnknown": "Backend selected during install", + "voiceLibrary.modelSetup.install": "安装", + "voiceLibrary.modelSetup.installing": "正在安装…", + "voiceLibrary.modelSetup.loadFailed": "Compatible recommendations could not be loaded.", + "voiceLibrary.modelSetup.noneAvailable": "No compatible uninstalled model is available in the configured galleries.", + "voiceLibrary.modelSetup.capabilityNote": "Recommendations come from model capability metadata, not a browser-side model list.", + "voiceLibrary.api.title": "API usage and compatible models", + "voiceLibrary.api.summary": "Use this voice outside the browser or check model support", + "voiceLibrary.api.body": "Send the profile’s stable voice URI with each speech request. LocalAI resolves the private reference audio and transcript at generation time; the settings of your models are not changed.", + "voiceLibrary.api.compatibleModels": "Compatible installed models", + "voiceLibrary.api.noInstalledModels": "None yet. Install one of the server-verified models above before sending this request.", + "voiceLibrary.api.curlExample": "cURL example", + "voiceLibrary.api.copy": "Copy request", + "voiceLibrary.api.endpointNote": "The same model, input, and voice fields also work with POST /tts.", + "voiceLibrary.actions.create": "Add a speech voice", + "voiceLibrary.actions.createFirst": "Add your first speech voice", + "voiceLibrary.actions.retry": "重试", + "voiceLibrary.actions.clearFilters": "清除筛选", + "voiceLibrary.actions.useInTTS": "Use in Text to Speech", + "voiceLibrary.actions.delete": "Delete voice", + "voiceLibrary.actions.installModel": "Install a compatible model", + "voiceLibrary.actions.browseModels": "Browse all models", + "voiceLibrary.deleteDialog.title": "Delete this voice?", + "voiceLibrary.deleteDialog.message": "“{{name}}” and its reference audio will be permanently removed. Models set up with this voice are not changed.", + "voiceLibrary.deleteDialog.deleting": "Deleting…", + "voiceLibrary.toasts.deleted": "Deleted {{name}}", + "voiceLibrary.toasts.installStarted": "Installing {{name}}. Progress is available in Operations.", + "voiceLibrary.toasts.installFailed": "Could not start installation: {{message}}", + "voiceLibrary.toasts.apiCopied": "API request copied", + "voiceLibrary.toasts.copyFailed": "Could not copy the API request", + "voiceCreate.title": "Add a speech voice", + "voiceCreate.subtitle": "A speech voice is one or more clean reference clips and their exact transcripts. Text-to-speech models that accept reference audio can speak with it.", + "voiceCreate.references.add": "Add another reference", + "voiceCreate.references.additional": "Reference {{number}}", + "voiceCreate.references.remove": "移除", + "voiceCreate.sections.reference.title": "Reference audio", + "voiceCreate.sections.reference.body": "Record in the browser or upload an existing clip. Speech should be clean, natural, and from one speaker.", + "voiceCreate.sections.details.title": "Identity and transcript", + "voiceCreate.sections.details.body": "Give admins a recognizable name and transcribe the clip exactly, including fillers and punctuation.", + "voiceCreate.sections.consent.title": "Authorization", + "voiceCreate.sections.consent.body": "Voice cloning can be sensitive. Confirm permission before this reference is saved.", + "voiceCreate.audio.label": "Voice reference", + "voiceCreate.audio.normalizing": "Preparing a secure PCM WAV…", + "voiceCreate.audio.preview": "Normalized reference", + "voiceCreate.audio.tooLarge": "This file is larger than 50 MiB.", + "voiceCreate.audio.durationError": "Reference audio must be between 1 second and 2 minutes.", + "voiceCreate.audio.decodeError": "This browser could not decode the selected audio file.", + "voiceCreate.audio.qualityReady": "Recommended length", + "voiceCreate.audio.qualityHint": "6–30 seconds usually produces a stronger clone", + "voiceCreate.fields.name": "Voice name", + "voiceCreate.fields.namePlaceholder": "e.g. Documentary narrator", + "voiceCreate.fields.language": "Language (optional)", + "voiceCreate.fields.languagePlaceholder": "e.g. en-US", + "voiceCreate.fields.description": "Description (optional)", + "voiceCreate.fields.descriptionPlaceholder": "Tone, source, or intended use", + "voiceCreate.fields.transcript": "Exact transcript", + "voiceCreate.fields.transcriptPlaceholder": "Type every word spoken in the reference clip…", + "voiceCreate.fields.transcriptHint": "Match the recording exactly. Do not rewrite grammar or remove pauses and fillers.", + "voiceCreate.consent.title": "I confirm that this voice may be cloned", + "voiceCreate.consent.body": "I have the speaker’s permission or another lawful basis to store and use this recording for synthesized speech.", + "voiceCreate.readiness.title": "Profile readiness", + "voiceCreate.readiness.body": "A voice becomes available in Text to Speech when every required item is complete.", + "voiceCreate.readiness.audio": "Valid PCM-WAV reference", + "voiceCreate.readiness.quality": "6–30 second quality window", + "voiceCreate.readiness.name": "Recognizable voice name", + "voiceCreate.readiness.transcript": "Exact reference transcript", + "voiceCreate.readiness.consent": "Authorization confirmed", + "voiceCreate.privacy.title": "Private by default. ", + "voiceCreate.privacy.body": "Reference audio is stored in LocalAI’s data directory with restricted file permissions and is never exposed as a model-folder path.", + "voiceCreate.actions.back": "Back to library", + "voiceCreate.actions.cancel": "取消", + "voiceCreate.actions.save": "Save voice", + "voiceCreate.actions.saving": "Saving…", + "voiceCreate.toasts.created": "{{name}} is ready to use", + "clip.mic": "microphone", + "clip.camera": "camera", + "clip.record": "Record", + "clip.takePhoto": "Take a photo", + "clip.chooseAudio": "Choose a file", + "clip.choosePhoto": "Choose a photo", + "clip.readAloud": "Read this aloud, normal pace", + "clip.remove": "移除", + "clip.blocked": "The {{thing}} is blocked. Allow it in the browser's site settings and try again, or choose a file instead.", + "clip.insecure": "A browser only opens the {{thing}} on https or on localhost. You are on {{origin}}. Open this page through localhost or add TLS, or choose a file.", + "clip.unsupported": "This browser cannot open the {{thing}}. Choose a file instead.", + "clip.missing": "No {{thing}} was found on this device. Choose a file instead.", + "clip.failed": "The {{thing}} could not be opened. Choose a file instead.", + "clip.allowed": "Microphone allowed.", + "clip.capture": "Capture", + "clip.cancel": "取消", + "clip.recording": "Recording, {{s}} s", + "clip.micReady": "Microphone ready", + "clip.stop": "停止", + "clip.start": "Start recording", + "clip.recorded": "Recording", + "clip.photographed": "Photo", + "clip.uploaded": "Uploaded file", + "clip.goodLength": "good length", + "clip.short": "short", + "clip.checkLevel": "check level", + "clip.preview": "预览", + "clip.photoAlt": "The photo you chose", + "same.text": "The two {{print}}s are {{distance}} apart. Anything under {{cutoff}} counts as the same person.", + "same.distance": "distance", + "word.strong": "Strong match", + "word.likely": "Likely match", + "word.close": "Close call", + "word.near": "Just past the cut-off", + "scale.aria": "Distance scale with the cut-off at {{cutoff}}. Lower means more alike. {{dots}}", + "scale.cutoff": "cut-off {{value}}", + "scale.axis": "distance between {{print}}s, lower means more alike", + "scale.oneDot": "one dot per enrolled {{person}}", + "scale.onePair": "one dot for the pair", + "ranks.aria": "Closest people, nearest first", + "ranks.within": "within cut-off", + "ranks.far": "too far", + "how.title": "How to read this.", + "how.body": "Distance is how far apart two {{print}}s are, 0 meaning identical. The cut-off is the line between same and different. The word Strong or Likely says how far inside the line a result sits. It is not a probability.", + "stored.title": "What is stored, and where", + "toast.enrolled": "Enrolled {{name}}", + "toast.enrolledBoth": "Enrolled {{name}} and saved a speech voice", + "toast.tooBig": "The copy was too large for browser storage, so it was not kept.", + "toast.speechFailed": "Enrolled, but the speech voice could not be saved: {{message}}", + "toast.removing": "{{name}} will be removed.", + "toast.removed": "Removed {{name}}", + "toast.alreadyGone": "{{name}} was already gone from the server.", + "error.retry": "重试", + "error.traces": "Open traces", + "needed.badge": "Not enabled on this server", + "needed.backend": "backend {{backend}}", + "needed.install": "安装", + "needed.installing": "正在安装", + "needed.started": "Installing {{name}}. Progress is in Operations.", + "needed.loadFailed": "Models could not be listed.", + "needed.noneListed": "No matching model is listed in the galleries.", + "needed.browse": "Browse all models", + "needed.askAdmin": "Ask an administrator to install one.", + "needed.whatTitle": "What turns this on", + "off.badge": "Not enabled for your account", + "off.voice.title": "Voice recognition is off for your account", + "off.voice.text": "An administrator has not allowed you to use voice recognition.", + "off.voice.what": "An administrator can turn on the Voice recognition permission for your user in Users and keys.", + "recording.title": "Name the speakers in a recording", + "recording.text": "The diarization workspace finds who spoke when in a recording. From there you can remember a speaker, and that voice joins the Speakers list here.", + "recording.step1": "Open the workspace, add a recording and tick Prepare speakers to remember.", + "recording.step2": "Run diarization. Each speaker with enough clean speech gets a Name and remember button.", + "recording.step3": "Name a speaker and remember it. That voice then appears under Speakers, ready for Who is this.", + "recording.open": "Open the diarization workspace", + "recording.off": "Diarization is not turned on for your account.", + "recording.warning": "Remembered voices are shared on this server and are lost when it restarts. Nothing is remembered automatically." +} diff --git a/addons/voice/ui/test/render.test.mjs b/addons/voice/ui/test/render.test.mjs index b3e386ace5..ec8ed8aa1d 100644 --- a/addons/voice/ui/test/render.test.mjs +++ b/addons/voice/ui/test/render.test.mjs @@ -94,3 +94,11 @@ test('render: each tab draws exactly one h1, the one Voice title (the add form k assert.deepEqual(h1s(renderToString(React.createElement(addon.Component, { page: 'voice', path: 'library' }))), ['Voices']) assert.deepEqual(h1s(renderToString(React.createElement(addon.Component, { page: 'voice', path: 'library/new' }))), ['Add a speech voice']) }) + +test('render: the page reads in the language of the web UI, with English for a key it lacks', async () => { + const it = await load({ language: () => 'it' }) + const addon = await it.mod.createAddon(it.sdk) + const html = renderToString(React.createElement(addon.Component, { page: 'voice', path: 'recognition' })) + assert.match(html, /Riconoscimento/) + assert.doesNotMatch(html, />Recognition { await buildAddonBundles(['memory']) }) -test('the add-on page has a notice that memory is built in, with a link to Settings > Memory', async ({ page }) => { +async function serveMemory(page) { await page.route('**/api/auth/status', route => route.fulfill({ json: sessionFor(ADMIN, { addons: true }) })) await stubAddonRoutes(page) await page.route(/\/api\/addons\/memory\/assets\//, (route) => { @@ -25,6 +25,10 @@ test('the add-on page has a notice that memory is built in, with a link to Setti }) await page.route('**/api/addons/memory/user-settings', route => route.fulfill({ json: { settings: [{ key: 'save-mode', values: ['ask', 'auto', 'never'], default: 'ask', value: 'ask' }] } })) await mockAddons(page, { apps: [packagedApp('memory')] }) +} + +test('the add-on page has a notice that memory is built in, with a link to Settings > Memory', async ({ page }) => { + await serveMemory(page) await page.goto('/app/addons/memory/memory') const notice = page.getByTestId('memory-builtin-notice') await expect(notice).toBeVisible() @@ -34,3 +38,14 @@ test('the add-on page has a notice that memory is built in, with a link to Setti await notice.getByRole('button', { name: 'Open Settings > Memory' }).click() await expect(page).toHaveURL(/\/app\/account\?tab=memory$/) }) + +test('the add-on page reads in the language of the web UI', async ({ page }) => { + await page.addInitScript(() => localStorage.setItem('localai-language', 'it')) + await serveMemory(page) + await page.goto('/app/addons/memory/memory') + await expect(page.getByTestId('memory-page')).toContainText('Memoria breve') + await expect(page.getByTestId('memory-builtin-notice')).toContainText('La memoria è integrata') + await expect(page.getByTestId('memory-save')).toContainText('Salvare una nota') + await expect(page.getByTestId('memory-open')).toHaveText('Apri le mie note') + await expect(page.getByTestId('memory-page')).not.toContainText('Short memory') +}) diff --git a/docs/content/features/addons.md b/docs/content/features/addons.md index 1f3e32589a..95c37e51f7 100644 --- a/docs/content/features/addons.md +++ b/docs/content/features/addons.md @@ -5144,6 +5144,12 @@ when a language lacks a bundle, a bundle lacks a key of English or has an extra from English, or a value is empty. There is no baseline: add a key to English and every language in the same change. +The Voice, Face, Fine-tuning, Quantization and Memory pages read their words from the same kind of bundle +(`addons//ui/src/i18n/..json`, flat keys). A list of notes is stored as numbered keys +(`voice.why.0`, `voice.stored.0.where`) and the page puts it back together, so each item is translated like any +other text. A string that has no translation yet repeats the English text in that language's bundle, which +satisfies the gate and shows English; replace it when a translation exists. + Compile an add-on with classic JSX transformed to `React.createElement` over `sdk.React`, inside the `createAddon` factory. Do not import a JSX runtime. The fixture packages under `tests/addons/fixture/ui` are written in that form.