Laut Babele 2.8+ Developer Migration Guide muessen register()/
registerConverters() vor dem Aufbau von Babeles eigener Runtime-Session
erfolgen, ueber einen dedizierten Hook 'babele.init' - nicht in Foundrys
allgemeinem 'init'-Hook. Registrierungen, die zu spaet erfolgen, werden
seit Babele 2.8+ still verworfen ('late calls are rejected'), sobald die
Runtime-Session installiert ist.
Das erklaert das beobachtete Verhalten vollstaendig: Babele las unsere
Uebersetzungsdateien zwar erfolgreich ein (Konsole zeigte 'successfully
loaded'), aber ohne erfolgreiche Registrierung blieb inspectMapping()
fuer betroffene Packs leer (layers: [], effective.mapping: {}), und
isTranslated()/translate() lieferten false/{} zurueck - obwohl die
Match-Daten (id/name) nachweislich korrekt waren. Journals funktionierten
vermutlich ueber eingebaute Babele-Standardmappings, die keine eigene
Registrierung benoetigen.
Fix: game.babele.register(...) und game.babele.registerConverters(...)
laufen jetzt in einem eigenen Hooks.once('babele.init', ...) statt im
bisherigen Hooks.once('init', ...). Ausserdem den fehlerhaften Typ-Check
'game.babele !== "undefined"' (String-Vergleich statt typeof) korrigiert.
Version auf 8.6.15 angehoben.
661 lines
30 KiB
JavaScript
661 lines
30 KiB
JavaScript
/************************************************************************************/
|
|
import loadScripts from "./loadScripts.js";
|
|
import statParserFR from "./import-stat-2.js";
|
|
|
|
/************************************************************************************/
|
|
let compmod = "wfrp4e-core";
|
|
const vo_conditions = {
|
|
"ablaze": "Ablaze",
|
|
"bleeding": "Bleeding",
|
|
"blinded": "Blinded",
|
|
"broken": "Broken",
|
|
"deafened": "Deafened",
|
|
"entangled": "Entangled",
|
|
"fatigued": "Fatigued",
|
|
"poisoned": "Poisoned",
|
|
"prone": "Prone",
|
|
"stunned": "Stunned",
|
|
"surprised": "Surprised",
|
|
"unconscious": "Unconscious",
|
|
"grappling": "Grappling",
|
|
"fear": "Fear",
|
|
"defeated": "Defeated"
|
|
}
|
|
|
|
// Map English characteristic names (as they appear in spell formulas) to WFRP4e abbreviations
|
|
const __CHAR_EN_TO_ABBR = {
|
|
"weapon skill": "ws",
|
|
"ballistic skill": "bs",
|
|
"strength": "s",
|
|
"toughness": "t",
|
|
"initiative": "i",
|
|
"agility": "ag",
|
|
"dexterity": "dex",
|
|
"intelligence": "int",
|
|
"willpower": "wp",
|
|
"fellowship": "fel"
|
|
}
|
|
|
|
/************************************************************************************/
|
|
export class WFRP4FrTranslation {
|
|
|
|
static parseSpellContent(spell) {
|
|
if (spell.system.range?.value) {
|
|
spell.system.range.value = this.processSpellContent(spell.system.range.value)
|
|
}
|
|
if (spell.system.damage?.value) {
|
|
spell.system.damage.value = this.processSpellContent(spell.system.damage.value)
|
|
}
|
|
if (spell.system.duration?.value) {
|
|
spell.system.duration.value = this.processSpellContent(spell.system.duration?.value)
|
|
}
|
|
if (spell.system.range?.value) {
|
|
spell.system.range.value = this.processSpellContent(spell.system.range?.value)
|
|
}
|
|
if (spell.system.target?.value) {
|
|
spell.system.target.value = this.processSpellContent(spell.system.target?.value)
|
|
}
|
|
}
|
|
|
|
static processSpellContent(value) {
|
|
if (value == "") return ""; // Hop !
|
|
if (value == "Touch") return "Berührung"; // Hop !
|
|
if (value == "You") return "Du"; // Hop !
|
|
if (value == "Instant") return "Sofort"; // Hop !
|
|
let translw = value;
|
|
let re = /(.*)\s+[Bb]onus\s*(.*)/i; // (.*) at end captures modifiers like "+4"
|
|
let res = re.exec(value);
|
|
let unit = "";
|
|
if (res) { // Test "<charac> Bonus <modifier>" pattern
|
|
if (res[1]) { // We have char name, then convert it
|
|
const charEN = res[1].trim().toLowerCase();
|
|
const abbr = __CHAR_EN_TO_ABBR[charEN];
|
|
if (abbr && game.wfrp4e?.config?.characteristicsBonus?.[abbr]) {
|
|
// Use the localized German bonus name from config (already resolved by localizeConfig at i18nInit)
|
|
translw = game.wfrp4e.config.characteristicsBonus[abbr];
|
|
} else {
|
|
translw = game.i18n.localize(res[1].trim());
|
|
translw = translw + "bonus";
|
|
}
|
|
}
|
|
unit = res[2]; // may be "+4", "-2", "2", "yards", etc.
|
|
} else {
|
|
re = /(\d+) (\w+)/i;
|
|
res = re.exec(value);
|
|
if (res) { // Test : "<number> <unit>" pattern
|
|
translw = res[1];
|
|
unit = res[2];
|
|
} else { // Test
|
|
re = /(\w+) (\w+)/i;
|
|
res = re.exec(value);
|
|
if (res) { // Test : "<charac> <unit>" pattern
|
|
translw = game.i18n.localize(res[1].trim());
|
|
unit = res[2];
|
|
}
|
|
}
|
|
}
|
|
if (unit == "hour") unit = "stunde";
|
|
if (unit == "hours") unit = "stunden";
|
|
if (unit == "days") unit = "tage";
|
|
if (unit == "yard") unit = "meter";
|
|
if (unit == "yards") unit = "meter";
|
|
if (unit == "Bonus") { // Another weird management
|
|
console.log("Translating bonus", unit);
|
|
translw = translw + " Bonus";
|
|
} else if (unit && /^[+\-*\/]/.test(unit)) {
|
|
translw += unit; // No space before operators like "+4"
|
|
} else {
|
|
translw += " " + unit;
|
|
}
|
|
return translw;
|
|
}
|
|
}
|
|
|
|
/************************************************************************************/
|
|
Hooks.once('init', () => {
|
|
|
|
// Check various settings in the installation
|
|
game.modules.forEach((module, id) => {
|
|
if (id == "wfrp4e-core" && module.active) {
|
|
compmod = "wfrp4e-core";
|
|
}
|
|
});
|
|
game.wfrp4efr = {
|
|
compmod: compmod,
|
|
vo_conditions: vo_conditions
|
|
}
|
|
|
|
game.wfrp4e.apps.StatBlockParser.parseStatBlock = async function (statString, type = "npc") {
|
|
return statParserFR(statString, type);
|
|
}
|
|
|
|
console.log("WFRP4E-DE | Loading Babele translation module ...");
|
|
loadScripts();
|
|
|
|
});
|
|
|
|
/************************************************************************************/
|
|
// Babele registration must happen in Babele's own "babele.init" hook (fires before
|
|
// Babele builds its runtime session/mapping layers). Registering in Foundry's
|
|
// "init" hook is too late for Babele 2.8+: once the runtime session is installed,
|
|
// late register()/registerConverters() calls are silently rejected, leaving the
|
|
// pack "loaded" (JSON parsed) but with no effective mapping applied - which is
|
|
// exactly the "everything stays English except journals" symptom.
|
|
// See: https://gitlab.com/riccisi/foundryvtt-babele/-/raw/master/DEVELOPER_MIGRATION_2_8_0.md
|
|
Hooks.once('babele.init', () => {
|
|
|
|
if (typeof game.babele !== 'undefined') {
|
|
|
|
game.babele.register({
|
|
module: 'wh4-de-translation',
|
|
lang: 'de',
|
|
dir: 'compendium'
|
|
})
|
|
|
|
// Pre-define functions for cross-converter calls (Babele 2.8+ wraps converters
|
|
// as objects, so game.babele.converters.X() no longer works as a plain call)
|
|
const career_skills_fn = (skills_list) => {
|
|
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("skill")
|
|
//DEBUG: console.log( "Thru here ...", compendium, skills_list);
|
|
if (skills_list) {
|
|
let i;
|
|
let len = skills_list.length;
|
|
let re = /(.*)\((.*)\)/i;
|
|
for (i = 0; i < len; i++) {
|
|
skills_list[i] = skills_list[i].trim();
|
|
for (let compData of validCompendiums) {
|
|
let translItem = game.babele.translate(compData.metadata.id, { name: skills_list[i], type: "skill" }, true)
|
|
let transl = translItem?.name || undefined
|
|
if (!transl) transl = skills_list[i]
|
|
if (transl == skills_list[i]) {
|
|
let res = re.exec(skills_list[i]);
|
|
if (res) {
|
|
let subword = game.i18n.localize(res[2].trim());
|
|
let s1 = res[1].trim() + " ()";
|
|
translItem = game.babele.translate(compData.metadata.id, { name: s1, type: "skill" }, true)
|
|
let translw = translItem?.name || undefined
|
|
if (translw && translw != s1) {
|
|
let res2 = re.exec(translw);
|
|
transl = res2[1].trim() + " (" + subword + ")";
|
|
} else {
|
|
s1 = res[1].trim() + " ( )";
|
|
translItem = game.babele.translate(compData.metadata.id, { name: s1, type: "skill" }, true)
|
|
translw = translItem?.name || undefined
|
|
if (translw) {
|
|
let res2 = re.exec(translw);
|
|
transl = res2[1].trim() + " (" + subword + ")";
|
|
} else {
|
|
transl = res[1].trim() + " (" + subword + ")";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
skills_list[i] = transl;
|
|
if (translItem?.system)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return skills_list
|
|
};
|
|
|
|
const career_careergroup_fn = (value) => {
|
|
if (value == 'Slayer') return "Slayer";
|
|
if (value == 'Druidic Priest') return "Druide";
|
|
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("career");
|
|
for (let compData of validCompendiums) {
|
|
let newName = game.babele.translate(compData.metadata.id, { name: value }).name;
|
|
if (!newName) newName = value;
|
|
return newName;
|
|
}
|
|
ui.notifications.error("Das Übersetzen der Karriere " + value + " ist nicht möglich. Sie ist wahrscheinlich nicht übersetzt.", { permanent: true });
|
|
return value;
|
|
};
|
|
|
|
game.babele.registerConverters({
|
|
|
|
"career_skills": career_skills_fn,
|
|
"role_skills": (skills) => {
|
|
let skills_list = skills.split(',');
|
|
let skillsFR = career_skills_fn(skills_list);
|
|
return skillsFR.join(', ')
|
|
},
|
|
"process_effects": (effectsData, translations, data, tc, tc_translations) => {
|
|
//console.log("Effects :", effectsData, translations, data, tc, tc_translations)
|
|
for (let e of effectsData) {
|
|
// Foundry v13 requires name; migrate legacy data where only label was stored
|
|
if (e.name == null) e.name = e.label || "";
|
|
let origName = e.name
|
|
// Symptom effects have their own name (Fever, Malaise, etc.) — don't overwrite with the parent item name
|
|
if (e.flags?.wfrp4e?.symptom) {
|
|
let symName = e.name;
|
|
let gravity = "";
|
|
if (symName.includes("(") && symName.includes(")")) {
|
|
let re = /(.*) +\((.*)\)/i;
|
|
let res = re.exec(symName);
|
|
if (res) {
|
|
symName = res[1].trim();
|
|
gravity = " (" + game.i18n.localize(res[2].trim()) + ")";
|
|
}
|
|
}
|
|
e.name = game.i18n.localize(symName) + gravity;
|
|
} else {
|
|
e.name = tc_translations.name || game.i18n.localize(e.name) || e.label || ""
|
|
}
|
|
if (e.flags?.wfrp4e?.scriptData) {
|
|
for (let script of e.flags.wfrp4e.scriptData) {
|
|
if (script?.label) {
|
|
// Quand le label du script est strictement identique au nom de l'item concerné
|
|
if (script.label.toLowerCase() == origName.toLowerCase()) {
|
|
script.label = e.name
|
|
} else if (script.label.toLowerCase().includes("tests to affect")) {
|
|
script.label = script.label.replace("Tests to affect", "Würfe zum Beeinflussen von ")
|
|
} else if (script.label.toLowerCase().includes("using torn muscle")) {
|
|
script.label = script.label.replace("Using Torn Muscle", "Verwende gerissenen Muskel ")
|
|
} else {
|
|
script.label = game.i18n.localize(script.label)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return effectsData
|
|
},
|
|
"resultConverter": (results, translated) => {
|
|
//console.log("STUF PARSING", results, translated)
|
|
if (translated) {
|
|
for (let data of results) {
|
|
if (translated[`${data.range[0]}-${data.range[1]}`]) {
|
|
data.description = translated[`${data.range[0]}-${data.range[1]}`]
|
|
}
|
|
}
|
|
return results
|
|
}
|
|
// Auto patch
|
|
if (results[0].description.includes("wfrp4e-core.career-descriptions")) {
|
|
results[0].description = "wfrp4e-core.journals"
|
|
}
|
|
if (results[0].description.includes("wfrp4e-core.journal")) {
|
|
for (let data of results) {
|
|
let career = data.description.match(/{(.*)}/)
|
|
//console.log(">>>>>", career)
|
|
if (career?.[1]) {
|
|
let careerFR = career_careergroup_fn(career[1])
|
|
data.description = data.description.replace(career[1], careerFR)
|
|
}
|
|
}
|
|
}
|
|
if (results?.[0].documentUuid) {
|
|
// Babele 2.8+ handles documentUuid resolution via its own mapping system
|
|
return results
|
|
}
|
|
return results
|
|
},
|
|
|
|
"npc_details": (details) => {
|
|
//console.log("DETAILS: ", details);
|
|
let newDetails = foundry.utils.duplicate(details);
|
|
if (details.species?.value)
|
|
newDetails.species.value = game.i18n.localize(details.species.value);
|
|
if (details.gender?.value)
|
|
newDetails.gender.value = game.i18n.localize(details.gender.value);
|
|
if (details.class?.value)
|
|
newDetails.class.value = game.i18n.localize(details.class.value);
|
|
return newDetails;
|
|
},
|
|
|
|
"career_talents": (talents_list) => {
|
|
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("talent")
|
|
let i;
|
|
if (talents_list) {
|
|
let len = talents_list.length;
|
|
let re = /(.*)\((.*)\)/i;
|
|
for (i = 0; i < len; i++) {
|
|
for (let compData of validCompendiums) {
|
|
let translItem = game.babele.translate(compData.metadata.id, { name: talents_list[i] }, true);
|
|
// Specific case management
|
|
if (talents_list[i] === "Trapper") {
|
|
translItem = game.babele.translate(compData.metadata.id, { name: "a7v422EZcOUUC20X" }, true);
|
|
}
|
|
if (talents_list[i] === "Inspiring") {
|
|
translItem = game.babele.translate(compData.metadata.id, { name: "WCXnFSV4WOSmzzc4" }, true);
|
|
}
|
|
//console.log("Search talent name:", compData.metadata.id, talents_list[i], translItem);
|
|
let transl = translItem?.name || undefined
|
|
if (!transl) transl = talents_list[i]
|
|
if (transl == talents_list[i]) {
|
|
let res = re.exec(talents_list[i]);
|
|
if (res) {
|
|
let subword = game.i18n.localize(res[2].trim());
|
|
let s1 = res[1].trim(); // No () in talents table
|
|
translItem = game.babele.translate(compData.metadata.id, { name: s1 }, true)
|
|
let translw = translItem?.name || undefined
|
|
//console.log("Search talent name:", compData.metadata.id, s1, translw);
|
|
if (translw && translw != s1) {
|
|
transl = translw.trim() + " (" + subword + ")";
|
|
}
|
|
}
|
|
}
|
|
talents_list[i] = transl;
|
|
if (translItem?.system)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return talents_list;
|
|
},
|
|
"npc_characteristics": (chars) => { // Auto-convert char names in the sheet
|
|
for (let key in chars) {
|
|
let char = chars[key];
|
|
//console.log("Was here !", key, char );
|
|
let abrev = char["abrev"];
|
|
let toTransl = "CHAR." + abrev;
|
|
if (game.i18n.localize(toTransl) != toTransl) { // Manages unknown language
|
|
char["label"] = game.i18n.localize("CHAR." + abrev);
|
|
char["abrev"] = game.i18n.localize("CHARAbbrev." + abrev);
|
|
}
|
|
}
|
|
return chars;
|
|
},
|
|
"bestiary_traits": (beast_traits, translations) => {
|
|
if (!beast_traits) {
|
|
console.log("No beast traits found here ...")
|
|
return beast_traits
|
|
}
|
|
//console.log("TRANS:", beast_traits)
|
|
// Normalize: Foundry v13 requires ActiveEffect.name; migrate legacy v12 data
|
|
for (let trait_en of beast_traits) {
|
|
if (trait_en.effects) {
|
|
for (let eff of trait_en.effects) {
|
|
if (eff.name == null) eff.name = eff.label || "";
|
|
}
|
|
}
|
|
}
|
|
for (let trait_en of beast_traits) {
|
|
let special = "";
|
|
let nbt = "";
|
|
let name_en = trait_en.name.replace(/\r/g, '').trim(); // strip \r (including internal) in some traits name
|
|
if (!trait_en.name || trait_en.name.length == 0) {
|
|
console.log("Wrong item name found!!!!")
|
|
continue
|
|
}
|
|
//console.log(">>>>>>>> Parsing", trait_en.name)
|
|
if (trait_en.type == "trait") {
|
|
//console.log("Trait translation", compmod, trait_en)
|
|
if (name_en.includes("Tentacles")) { // Process specific Tentacles case
|
|
let re = /(.d*)x Tentacles/i;
|
|
let res = re.exec(name_en);
|
|
if (res?.[1])
|
|
nbt = res[1] + "x ";
|
|
name_en = "Tentacles";
|
|
} else if (name_en.includes("(") && name_en.includes(")")) { // Then process specific traits name with (xxxx) inside
|
|
let re = /(.*) \((.*)\)/i;
|
|
let res = re.exec(name_en);
|
|
if (!res) { console.warn("WFRP4E-DE | bestiary_traits: regex failed for trait:", name_en); }
|
|
else {
|
|
name_en = res[1]; // Get the root traits name
|
|
special = " (" + game.i18n.localize(res[2].trim()) + ")"; // And the special keyword
|
|
}
|
|
}
|
|
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("trait")
|
|
for (let compData of validCompendiums) {
|
|
let trait_fr = game.babele.translate(compData.metadata.id, { name: name_en, system: { description: { value: trait_en.system.description.value } } }, true)
|
|
if (trait_fr?.name && trait_fr?.name != name_en) {
|
|
trait_fr.name = trait_fr.name || trait_en.name
|
|
trait_en.name = nbt + trait_fr.name + special;
|
|
if (trait_en.system?.description?.value && trait_fr.system?.description?.value) {
|
|
trait_en.system.description.value = trait_fr.system.description.value;
|
|
}
|
|
if (trait_en?.system?.specification && isNaN(trait_en.system.specification?.value)) { // This is a string, so translate it
|
|
//console.log("Translating : ", trait_en.system.specification.value);
|
|
trait_en.system.specification.value = game.i18n.localize(trait_en.system.specification.value.trim());
|
|
}
|
|
break // Translation has been found, skip other compendiums
|
|
}
|
|
}
|
|
} else if (trait_en.type == "skill") {
|
|
if (name_en.includes("(") && name_en.includes(")")) { // Then process specific skills name with (xxxx) inside
|
|
let re = /(.*) +\((.*)\)/i;
|
|
let res = re.exec(name_en);
|
|
if (!res) { console.warn("WFRP4E-DE | bestiary_traits: regex failed for skill:", name_en); }
|
|
else {
|
|
name_en = res[1].trim(); // Get the root skill name
|
|
special = " (" + game.i18n.localize(res[2].trim()) + ")"; // And the special keyword
|
|
}
|
|
}
|
|
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("skill")
|
|
for (let compData of validCompendiums) {
|
|
let trait_fr = game.babele.translate(compData.metadata.id, { name: name_en, system: { description: { value: trait_en.system.description.value } } }, true)
|
|
//console.log(">>>>> Skill ?", name_en, special, trait_fr.name, trait_fr);
|
|
if (trait_fr?.name && trait_fr?.name != name_en) {
|
|
trait_fr.name = trait_fr.name || name_en
|
|
trait_en.name = trait_fr.name + special;
|
|
trait_en.system.description.value = trait_fr.system.description.value;
|
|
break; // Translation has been found, skip other compendiums
|
|
}
|
|
}
|
|
} else if (trait_en.type == "prayer") {
|
|
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("prayer")
|
|
for (let compData of validCompendiums) {
|
|
let trait_fr = game.babele.translate(compData.metadata.id, { name: name_en, system: { description: { value: trait_en.system.description.value } } }, true)
|
|
if (trait_fr?.name && trait_fr?.name != name_en) {
|
|
//DEBUG : console.log(">>>>> Prayer ?", name_en, special, trait_fr.name );
|
|
WFRP4FrTranslation.parseSpellContent(trait_en)
|
|
trait_fr.name = trait_fr.name || name_en
|
|
trait_en.name = trait_fr.name + special;
|
|
if (trait_fr.system?.description?.value) {
|
|
trait_en.system.description.value = trait_fr.system.description.value;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else if (trait_en.type == "spell") {
|
|
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("spell")
|
|
for (let compData of validCompendiums) {
|
|
let trait_fr = game.babele.translate(compData.metadata.id, { name: name_en, system: { description: { value: trait_en.system.description.value } } }, true)
|
|
if (trait_fr?.name && trait_fr?.name != name_en) {
|
|
//DEBUG : console.log(">>>>> Spell ?", name_en, special, trait_fr);
|
|
WFRP4FrTranslation.parseSpellContent(trait_en)
|
|
trait_fr.name = trait_fr.name || name_en
|
|
trait_en.name = trait_fr.name + special;
|
|
if (trait_fr.system?.description?.value) {
|
|
trait_en.system.description.value = trait_fr.system.description.value;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else if (trait_en.type == "talent") {
|
|
if (name_en.includes("(") && name_en.includes(")")) { // Then process specific skills name with (xxxx) inside
|
|
let re = /(.*) +\((.*)\)/i;
|
|
let res = re.exec(name_en);
|
|
if (!res) { console.warn("WFRP4E-DE | bestiary_traits: regex failed for talent:", name_en); }
|
|
else {
|
|
name_en = res[1].trim(); // Get the root talent name, no parenthesis this time...
|
|
special = " (" + game.i18n.localize(res[2].trim()) + ")"; // And the special keyword
|
|
}
|
|
}
|
|
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("talent")
|
|
for (let compData of validCompendiums) {
|
|
if (name_en === "Trapper") {
|
|
name_en = "a7v422EZcOUUC20X"
|
|
}
|
|
if (name_en === "Inspiring") {
|
|
name_en = "WCXnFSV4WOSmzzc4"
|
|
}
|
|
trait_en.name = name_en // Reset the name to the original one
|
|
let trait_fr = game.babele.translate(compData.metadata.id, trait_en, true)
|
|
if (trait_fr?.name && trait_fr?.name != name_en) {
|
|
trait_fr.name = trait_fr.name || name_en // Security since babele v10
|
|
//console.log(">>>>> Talent ?", trait_fr, name_en, special, trait_fr.name);
|
|
if (trait_fr.name && (trait_fr.name == "Sprinter" || trait_fr.name != name_en)) { // Talent translated!
|
|
trait_en.name = trait_fr.name.trim() + special
|
|
if (trait_fr.system?.tests?.value) { // Why ???
|
|
trait_en.system.tests.value = trait_fr.system.tests.value;
|
|
}
|
|
if (trait_fr.system?.description?.value) { // Why ???
|
|
trait_en.system.description.value = trait_fr.system.description.value;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else if (trait_en.type == "career") {
|
|
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("career")
|
|
for (let compData of validCompendiums) {
|
|
let career_fr = game.babele.translate(compData.metadata.id, trait_en, true);
|
|
if (career_fr?.name && career_fr?.name != name_en) {
|
|
trait_en.name = career_fr.name || trait_en.name
|
|
// DEBG: console.log(">>>>> Career ?", career_fr.name );
|
|
trait_en.system = foundry.utils.duplicate(career_fr.system);
|
|
break;
|
|
}
|
|
}
|
|
} else if (trait_en.type == "vehicleRole") {
|
|
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("vehicleRole")
|
|
for (let compData of validCompendiums) {
|
|
let role_fr = game.babele.translate(compData.metadata.id, trait_en, true);
|
|
if (role_fr?.name && role_fr?.name != name_en) {
|
|
trait_en.name = role_fr.name || trait_en.name
|
|
// DEBG: console.log(">>>>> Role ?", role_fr.name );
|
|
trait_en.system = foundry.utils.duplicate(role_fr.system);
|
|
break;
|
|
}
|
|
}
|
|
} else if (trait_en.type == "trapping" || trait_en.type == "weapon" || trait_en.type == "armour" || trait_en.type == "container" || trait_en.type == "money") {
|
|
let validCompendiums = game.wfrp4e.tags.getPacksWithTag(["trapping"], ["weapon", "armour", "container", "money"])
|
|
for (let compData of validCompendiums) {
|
|
let trapping_fr = game.babele.translate(compData.metadata.id, { name: name_en, system: { description: { value: trait_en.system.description.value } } }, true);
|
|
if (trapping_fr?.name && trapping_fr?.name != name_en) {
|
|
//console.log(">>>>> Trapping ?", name_en, trapping_fr.name);
|
|
trait_en.name = trapping_fr.name || trait_en.name
|
|
if (trapping_fr.system?.description?.value) {
|
|
trait_en.system.description.value = trapping_fr.system.description.value
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//console.log(">>>>>>>><OUTPUT", beast_traits)
|
|
return beast_traits
|
|
},
|
|
// To avoid duplicateing class for all careers
|
|
"generic_localization": (value) => {
|
|
let ret = value
|
|
if (value) {
|
|
ret = game.i18n.localize(value.trim());
|
|
if (!ret) ret = value
|
|
}
|
|
return ret
|
|
},
|
|
"trapping_qualities_flaws": (value) => {
|
|
if (value) {
|
|
let newQF = [];
|
|
//console.log("ATOUTS", value, typeof(value));
|
|
let list = value
|
|
if (typeof (value) == "string") {
|
|
let myList = value.split(",")
|
|
list = []
|
|
for (let l of myList) {
|
|
list.push({ name: l.trim() })
|
|
}
|
|
}
|
|
for (let i = 0; i < list.length; i++) {
|
|
newQF[i] = foundry.utils.duplicate(list[i])
|
|
if (newQF[i].name == "Trap Blade") {
|
|
newQF[i].name = "TrapBlade"; // Auto-patch, without space!
|
|
//console.log("PATCHED", trim);
|
|
}
|
|
let oldName = newQF[i].name
|
|
newQF[i].name = game.i18n.localize(oldName)
|
|
if (!newQF[i].name) newQF[i].name = oldName
|
|
}
|
|
return newQF;
|
|
}
|
|
},
|
|
// Search back in careers the translated name of the groupe (as it is the name of the level career itself)
|
|
"career_careergroup": career_careergroup_fn,
|
|
|
|
"mutations_modifier": (value) => { // This is really UGLYYYY i know, but i started like this and discovered afterward that many strings were not easy to automate... Sorry :)
|
|
//console.log("Parsing mutation :", value);
|
|
if (!value) return;
|
|
value = value.toLowerCase();
|
|
value = value.replace("gain a broken condition if you fail a test derived from ", "Du erhälst einen Demoralisiert-Zustand wenn dir ein Wurf misslingt in ");
|
|
value = value.replace("weapon skill", "Kampfgeschick");
|
|
value = value.replace("ballistic skill", "Ballisitsche Fertigkeit");
|
|
value = value.replace("strength", "Stärke");
|
|
value = value.replace("toughness", "Widerstand");
|
|
value = value.replace("agility", "Gewandheit");
|
|
value = value.replace("dexterity", "Geschicklichkeit");
|
|
value = value.replace("willpower", "Willenskraft");
|
|
value = value.replace("fellowship", "Charisma");
|
|
value = value.replace("initiative", "Initiative");
|
|
value = value.replace("intelligence", "Intelligenz");
|
|
value = value.replace("armor points to the head", "RP am Kopf");
|
|
value = value.replace("subject to frenzy", "unterliegt Raserei");
|
|
value = value.replace("you do not scar", "Du bildest keine Narben");
|
|
value = value.replace("movement", "Bewegung");
|
|
value = value.replace("armor points to all locations", "RP am ganzen Körper");
|
|
value = value.replace("to any test when alone", "auf jeden Wurf, wenn du alleine bsit");
|
|
value = value.replace("track", "Spurenlesen");
|
|
value = value.replace("to any test not hurting another", "auf jeden Wurf, um anderen keine Schmerzen zuzufügen");
|
|
value = value.replace("on tests to hurt", "auf jeden Wurf, um anderen Schmerzen zuzufügen")
|
|
value = value.replace("to all language tests when speaking", "auf alle Sprache-Würfe beim Sprechen");
|
|
value = value.replace("on perception tests involving sight", "auf Wahrnehmungs-Würfe, bei denen es um die Sicht geht");
|
|
value = value.replace("to all Sociabilité tests", "auf alle gesellschaftlichen Würfe");
|
|
return value;
|
|
},
|
|
"talent_name": (name, translation) => {
|
|
console.log("NAME !!!", name, translation)
|
|
},
|
|
"effects": (effects, translations) => {
|
|
if (!effects) return;
|
|
if (!translations) return;
|
|
for (let i = 0; i < effects.length; i++) {
|
|
let effect = effects[i];
|
|
//console.log("EFFECT LABEL1:", effect );
|
|
effect.name = translations['label' + i];
|
|
}
|
|
return effects
|
|
},
|
|
"diseases_effects": (effects, translations) => {
|
|
if (!effects) return;
|
|
for (const element of effects) {
|
|
let effect = element;
|
|
let name = effect.name || effect.label;
|
|
let gravity = "";
|
|
if (name.includes("(") && name.includes(")")) { // Then process specific skills name with (xxxx) inside
|
|
let re = /(.*) +\((.*)\)/i;
|
|
let res = re.exec(name);
|
|
name = res[1].trim(); // Get the gravity
|
|
gravity = " (" + game.i18n.localize(res[2].trim()) + ")"; // And the special keyword
|
|
}
|
|
effect.name = game.i18n.localize(name) + gravity;
|
|
}
|
|
},
|
|
// Auto-translate duration
|
|
"spells_duration_range_target_damage": (value) => {
|
|
return WFRP4FrTranslation.processSpellContent(value);
|
|
},
|
|
// Auto-translate disease duration units
|
|
"disease_duration_unit": (unit) => {
|
|
return game.i18n.localize(unit).capitalize();
|
|
}
|
|
});
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
Hooks.once('ready', () => {
|
|
|
|
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter => {
|
|
console.log("ClassCounter loaded", moduleCounter)
|
|
moduleCounter.ClassCounter.registerUsageCount("wh4-de-translation")
|
|
}).catch(err =>
|
|
console.log("No stats available, giving up.")
|
|
)
|
|
|
|
});
|