diff --git a/lang/en.json b/lang/en.json index 63b18219..1bddbad3 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2347,6 +2347,42 @@ "playerMessage": "{user} rerolled their {name}" } }, + "ItemBrowser": { + "title": "Daggerheart Compendium Browser", + "hint": "Select a Folder in sidebar to start browsing through the compendium", + "searchPlaceholder": "Search...", + "columnName": "Name", + "tooltipFilters": "Filters", + "tooltipErase": "Erase", + "difficultyMin": "Difficulty (Min)", + "difficultyMax": "Difficulty (Max)", + "hitPointsMin": "Hit Points (Min)", + "hitPointsMax": "Hit Points (Max)", + "stressMin": "Stress (Min)", + "stressMax": "Stress (Max)", + "armorScoreMin": "Armor Score (Min)", + "armorScoreMax": "Armor Score (Max)", + "levelMin": "Level (Min)", + "levelMax": "Level (Max)", + "recallCostMin": "Recall Cost (Min)", + "recallCostMax": "Recall Cost (Max)", + "evasionMin": "Evasion (Min)", + "evasionMax": "Evasion (Max)", + "subtype": "Subtype", + "folders": { + "adversaries": "Adversaries", + "ancestries": "Ancestries", + "equipment": "Equipment", + "classes": "Classes", + "subclasses": "Subclasses", + "domainCards": "Domain Cards", + "communities": "Communities", + "environments": "Environments", + "beastforms": "Beastforms", + "features": "Features", + "items": "Items" + } + }, "Notifications": { "adversaryMissing": "The linked adversary doesn't exist in the world.", "beastformInapplicable": "A beastform can only be applied to a Character.", @@ -2406,7 +2442,8 @@ "beastformEquipWeapon": "You cannot use weapons while in a Beastform.", "loadoutMaxReached": "You've reached maximum loadout. Move atleast one domain card to the vault, or increase the limit in homebrew settings if desired.", "domainMaxReached": "You've reached the maximum domains for the class. Increase the limit in homebrew settings if desired.", - "insufficientResources": "You have insufficient resources", + "insufficientResources": "You don't have enough resources to use that action.", + "actionNoUsesRemaining": "That action doesn't have remaining uses.", "multiclassAlreadyPresent": "You already have a class and multiclass", "subclassesAlreadyPresent": "You already have a class and multiclass subclass", "noDiceSystem": "Your selected dice {system} does not have a {faces} dice" diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index f0ad98db..3a31bd4e 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -154,7 +154,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { Object.values(config).forEach(c => { const folder = { id: c.id, - label: c.label, + label: game.i18n.localize(c.label), selected: (!parent || parent.selected) && this.selectedMenu.path[depth] === c.id }; folder.folders = c.folders @@ -173,11 +173,16 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { folderPath = `${compendium}.folders.${folderId}`, folderData = foundry.utils.getProperty(config, folderPath); + const columns = ItemBrowser.getFolderConfig(folderData).map(col => ({ + ...col, + label: game.i18n.localize(col.label) + })); + this.selectedMenu = { path: folderPath.split('.'), data: { ...folderData, - columns: ItemBrowser.getFolderConfig(folderData) + columns: columns } }; @@ -237,6 +242,12 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { else if (typeof f.choices === 'function') { f.choices = f.choices(); } + + // Clear field label so template uses our custom label parameter + if (f.field && f.label) { + f.field.label = undefined; + } + f.name ??= f.key; f.value = this.presets?.filter?.[f.name]?.value ?? null; }); diff --git a/module/config/itemBrowserConfig.mjs b/module/config/itemBrowserConfig.mjs index 6e3c0dea..0cb42d2c 100644 --- a/module/config/itemBrowserConfig.mjs +++ b/module/config/itemBrowserConfig.mjs @@ -3,63 +3,63 @@ export const typeConfig = { columns: [ { key: "system.tier", - label: "Tier" + label: "DAGGERHEART.GENERAL.Tiers.singular" }, { key: "system.type", - label: "Type" + label: "DAGGERHEART.GENERAL.type" } ], filters: [ { key: "system.tier", - label: "Tier", + label: "DAGGERHEART.GENERAL.Tiers.singular", field: 'system.api.models.actors.DhAdversary.schema.fields.tier' }, { key: "system.type", - label: "Type", + label: "DAGGERHEART.GENERAL.type", field: 'system.api.models.actors.DhAdversary.schema.fields.type' }, { key: "system.difficulty", name: "difficulty.min", - label: "Difficulty (Min)", + label: "DAGGERHEART.UI.ItemBrowser.difficultyMin", field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty', operator: "gte" }, { key: "system.difficulty", name: "difficulty.max", - label: "Difficulty (Max)", + label: "DAGGERHEART.UI.ItemBrowser.difficultyMax", field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty', operator: "lte" }, { key: "system.resources.hitPoints.max", name: "hp.min", - label: "Hit Points (Min)", + label: "DAGGERHEART.UI.ItemBrowser.hitPointsMin", field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max', operator: "gte" }, { key: "system.resources.hitPoints.max", name: "hp.max", - label: "Hit Points (Max)", + label: "DAGGERHEART.UI.ItemBrowser.hitPointsMax", field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max', operator: "lte" }, { key: "system.resources.stress.max", name: "stress.min", - label: "Stress (Min)", + label: "DAGGERHEART.UI.ItemBrowser.stressMin", field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max', operator: "gte" }, { key: "system.resources.stress.max", name: "stress.max", - label: "Stress (Max)", + label: "DAGGERHEART.UI.ItemBrowser.stressMax", field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max', operator: "lte" }, @@ -69,70 +69,70 @@ export const typeConfig = { columns: [ { key: "type", - label: "Type" + label: "DAGGERHEART.GENERAL.type" }, { key: "system.secondary", - label: "Subtype", + label: "DAGGERHEART.UI.ItemBrowser.subtype", format: (isSecondary) => isSecondary ? "secondary" : (isSecondary === false ? "primary" : '-') }, { key: "system.tier", - label: "Tier" + label: "DAGGERHEART.GENERAL.Tiers.singular" } ], filters: [ { key: "type", - label: "Type", + label: "DAGGERHEART.GENERAL.type", choices: () => CONFIG.Item.documentClass.TYPES.filter(t => ["armor", "weapon", "consumable", "loot"].includes(t)).map(t => ({ value: t, label: t })) }, { key: "system.secondary", - label: "Subtype", + label: "DAGGERHEART.UI.ItemBrowser.subtype", choices: [ - { value: false, label: "Primary Weapon"}, - { value: true, label: "Secondary Weapon"} + { value: false, label: "DAGGERHEART.ITEMS.Weapon.primaryWeapon" }, + { value: true, label: "DAGGERHEART.ITEMS.Weapon.secondaryWeapon" } ] }, { key: "system.tier", - label: "Tier", - choices: [{ value: "1", label: "1"}, { value: "2", label: "2"}, { value: "3", label: "3"}, { value: "4", label: "4"}] + label: "DAGGERHEART.GENERAL.Tiers.singular", + choices: [{ value: "1", label: "1" }, { value: "2", label: "2" }, { value: "3", label: "3" }, { value: "4", label: "4" }] }, { key: "system.burden", - label: "Burden", + label: "DAGGERHEART.GENERAL.burden", field: 'system.api.models.items.DHWeapon.schema.fields.burden' }, { key: "system.attack.roll.trait", - label: "Trait", + label: "DAGGERHEART.GENERAL.Trait.single", field: 'system.api.models.actions.actionsTypes.attack.schema.fields.roll.fields.trait' }, { key: "system.attack.range", - label: "Range", + label: "DAGGERHEART.GENERAL.range", field: 'system.api.models.actions.actionsTypes.attack.schema.fields.range' }, { key: "system.baseScore", name: "armor.min", - label: "Armor Score (Min)", + label: "DAGGERHEART.UI.ItemBrowser.armorScoreMin", field: 'system.api.models.items.DHArmor.schema.fields.baseScore', operator: "gte" }, { key: "system.baseScore", name: "armor.max", - label: "Armor Score (Max)", + label: "DAGGERHEART.UI.ItemBrowser.armorScoreMax", field: 'system.api.models.items.DHArmor.schema.fields.baseScore', operator: "lte" }, { key: "system.itemFeatures", - label: "Features", - choices: () => [...Object.entries(CONFIG.DH.ITEM.weaponFeatures), ...Object.entries(CONFIG.DH.ITEM.armorFeatures)].map(([k,v]) => ({ value: k, label: v.label})), + label: "DAGGERHEART.GENERAL.features", + choices: () => [...Object.entries(CONFIG.DH.ITEM.weaponFeatures), ...Object.entries(CONFIG.DH.ITEM.armorFeatures)].map(([k, v]) => ({ value: k, label: v.label })), operator: "contains3" } ] @@ -149,54 +149,54 @@ export const typeConfig = { columns: [ { key: "system.type", - label: "Type" + label: "DAGGERHEART.GENERAL.type" }, { key: "system.domain", - label: "Domain" + label: "DAGGERHEART.GENERAL.Domain.single" }, { key: "system.level", - label: "Level" + label: "DAGGERHEART.GENERAL.level" } ], filters: [ { key: "system.type", - label: "Type", + label: "DAGGERHEART.GENERAL.type", field: 'system.api.models.items.DHDomainCard.schema.fields.type' }, { key: "system.domain", - label: "Domain", + label: "DAGGERHEART.GENERAL.Domain.single", field: 'system.api.models.items.DHDomainCard.schema.fields.domain', operator: "contains2" }, { key: "system.level", name: "level.min", - label: "Level (Min)", + label: "DAGGERHEART.UI.ItemBrowser.levelMin", field: 'system.api.models.items.DHDomainCard.schema.fields.level', operator: "gte" }, { key: "system.level", name: "level.max", - label: "Level (Max)", + label: "DAGGERHEART.UI.ItemBrowser.levelMax", field: 'system.api.models.items.DHDomainCard.schema.fields.level', operator: "lte" }, { key: "system.recallCost", name: "recall.min", - label: "Recall Cost (Min)", + label: "DAGGERHEART.UI.ItemBrowser.recallCostMin", field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost', operator: "gte" }, { key: "system.recallCost", name: "recall.max", - label: "Recall Cost (Max)", + label: "DAGGERHEART.UI.ItemBrowser.recallCostMax", field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost', operator: "lte" } @@ -206,50 +206,50 @@ export const typeConfig = { columns: [ { key: "system.evasion", - label: "Evasion" + label: "DAGGERHEART.GENERAL.evasion" }, { key: "system.hitPoints", - label: "Hit Points" + label: "DAGGERHEART.GENERAL.HitPoints.plural" }, { key: "system.domains", - label: "Domains" + label: "DAGGERHEART.GENERAL.Domain.plural" } ], filters: [ { key: "system.evasion", name: "evasion.min", - label: "Evasion (Min)", + label: "DAGGERHEART.UI.ItemBrowser.evasionMin", field: 'system.api.models.items.DHClass.schema.fields.evasion', operator: "gte" }, { key: "system.evasion", name: "evasion.max", - label: "Evasion (Max)", + label: "DAGGERHEART.UI.ItemBrowser.evasionMax", field: 'system.api.models.items.DHClass.schema.fields.evasion', operator: "lte" }, { key: "system.hitPoints", name: "hp.min", - label: "Hit Points (Min)", + label: "DAGGERHEART.UI.ItemBrowser.hitPointsMin", field: 'system.api.models.items.DHClass.schema.fields.hitPoints', operator: "gte" }, { key: "system.hitPoints", name: "hp.max", - label: "Hit Points (Max)", + label: "DAGGERHEART.UI.ItemBrowser.hitPointsMax", field: 'system.api.models.items.DHClass.schema.fields.hitPoints', operator: "lte" }, { key: "system.domains", - label: "Domains", - choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label})), + label: "DAGGERHEART.GENERAL.Domain.plural", + choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label })), operator: "contains2" } ] @@ -258,14 +258,14 @@ export const typeConfig = { columns: [ { key: "id", - label: "Class", + label: "TYPES.Item.class", format: (id) => { return ""; } }, { key: "system.spellcastingTrait", - label: "Spellcasting Trait" + label: "DAGGERHEART.ITEMS.Subclass.spellcastingTrait" } ], filters: [] @@ -274,22 +274,22 @@ export const typeConfig = { columns: [ { key: "system.tier", - label: "Tier" + label: "DAGGERHEART.GENERAL.Tiers.singular" }, { key: "system.mainTrait", - label: "Main Trait" + label: "DAGGERHEART.GENERAL.Trait.single" } ], filters: [ { key: "system.tier", - label: "Tier", + label: "DAGGERHEART.GENERAL.Tiers.singular", field: 'system.api.models.items.DHBeastform.schema.fields.tier' }, { key: "system.mainTrait", - label: "Main Trait", + label: "DAGGERHEART.GENERAL.Trait.single", field: 'system.api.models.items.DHBeastform.schema.fields.mainTrait' } ] @@ -304,20 +304,20 @@ export const compendiumConfig = { "adversaries": { id: "adversaries", keys: ["adversaries"], - label: "Adversaries", + label: "DAGGERHEART.UI.ItemBrowser.folders.adversaries", type: ["adversary"], listType: "adversaries" }, "ancestries": { id: "ancestries", keys: ["ancestries"], - label: "Ancestries", + label: "DAGGERHEART.UI.ItemBrowser.folders.ancestries", type: ["ancestry"], folders: { "features": { id: "features", keys: ["ancestries"], - label: "Features", + label: "DAGGERHEART.UI.ItemBrowser.folders.features", type: ["feature"] } } @@ -325,26 +325,26 @@ export const compendiumConfig = { "equipments": { id: "equipments", keys: ["armors", "weapons", "consumables", "loot"], - label: "Equipment", + label: "DAGGERHEART.UI.ItemBrowser.folders.equipment", type: ["armor", "weapon", "consumable", "loot"], listType: "items" }, "classes": { id: "classes", keys: ["classes"], - label: "Classes", + label: "DAGGERHEART.UI.ItemBrowser.folders.classes", type: ["class"], folders: { "features": { id: "features", keys: ["classes"], - label: "Features", + label: "DAGGERHEART.UI.ItemBrowser.folders.features", type: ["feature"] }, "items": { id: "items", keys: ["classes"], - label: "Items", + label: "DAGGERHEART.UI.ItemBrowser.folders.items", type: ["armor", "weapon", "consumable", "loot"], listType: "items" } @@ -354,27 +354,27 @@ export const compendiumConfig = { "subclasses": { id: "subclasses", keys: ["subclasses"], - label: "Subclasses", + label: "DAGGERHEART.UI.ItemBrowser.folders.subclasses", type: ["subclass"], listType: "subclasses" }, "domains": { id: "domains", keys: ["domains"], - label: "Domain Cards", + label: "DAGGERHEART.UI.ItemBrowser.folders.domainCards", type: ["domainCard"], listType: "cards" }, "communities": { id: "communities", keys: ["communities"], - label: "Communities", + label: "DAGGERHEART.UI.ItemBrowser.folders.communities", type: ["community"], folders: { "features": { id: "features", keys: ["communities"], - label: "Features", + label: "DAGGERHEART.UI.ItemBrowser.folders.features", type: ["feature"] } } @@ -382,20 +382,20 @@ export const compendiumConfig = { "environments": { id: "environments", keys: ["environments"], - label: "Environments", + label: "DAGGERHEART.UI.ItemBrowser.folders.environments", type: ["environment"] }, "beastforms": { id: "beastforms", keys: ["beastforms"], - label: "Beastforms", + label: "DAGGERHEART.UI.ItemBrowser.folders.beastforms", type: ["beastform"], listType: "beastforms", folders: { "features": { id: "features", keys: ["beastforms"], - label: "Features", + label: "DAGGERHEART.UI.ItemBrowser.folders.features", type: ["feature"] } } diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index f4d942b1..c224fff0 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -25,7 +25,7 @@ export default class CostField extends fields.ArrayField { config.costs = CostField.calcCosts.call(this, costs); const hasCost = CostField.hasCost.call(this, config.costs); if (config.isFastForward && !hasCost) - return ui.notifications.warn("You don't have the resources to use that action."); + return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.insufficientResources')); return hasCost; } diff --git a/module/data/fields/action/usesField.mjs b/module/data/fields/action/usesField.mjs index 3993ca3b..5c2bfb61 100644 --- a/module/data/fields/action/usesField.mjs +++ b/module/data/fields/action/usesField.mjs @@ -25,7 +25,7 @@ export default class UsesField extends fields.SchemaField { if (uses && !uses.value) uses.value = 0; config.uses = uses; const hasUses = UsesField.hasUses.call(this, config.uses); - if (config.isFastForward && !hasUses) return ui.notifications.warn("That action doesn't have remaining uses."); + if (config.isFastForward && !hasUses) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.actionNoUsesRemaining')); return hasUses; } diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 171255e2..83220307 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -13,7 +13,8 @@ export default class RegisterHandlebarsHelpers { hasProperty: foundry.utils.hasProperty, getProperty: foundry.utils.getProperty, setVar: this.setVar, - empty: this.empty + empty: this.empty, + pluralize: this.pluralize }); } static add(a, b) { @@ -64,7 +65,7 @@ export default class RegisterHandlebarsHelpers { return isNumerical ? (!result ? 0 : Number(result)) : result; } - static setVar(name, value, context) { + static setVar(name, value) { this[name] = value; } @@ -72,4 +73,20 @@ export default class RegisterHandlebarsHelpers { if (!(typeof object === 'object')) return true; return Object.keys(object).length === 0; } + + /** + * Pluralize helper that returns the appropriate localized string based on count + * @param {number} count - The number to check for plurality + * @param {string} baseKey - The base localization key (e.g., "DAGGERHEART.GENERAL.Target") + * @returns {string} The localized singular or plural string + * + * Usage: {{pluralize currentTargets.length "DAGGERHEART.GENERAL.Target"}} + * Returns: "Target" if count is exactly 1, "Targets" if count is 0, 2+, or invalid + */ + static pluralize(count, baseKey) { + const numericCount = Number(count); + const isSingular = !isNaN(numericCount) && numericCount === 1; + const key = isSingular ? `${baseKey}.single` : `${baseKey}.plural`; + return game.i18n.localize(key); + } } diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 88a72fb8..e19c1dea 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -6,7 +6,7 @@ type='text' name='name' value='{{document.name}}' - placeholder='Actor Name' + placeholder='{{localize "DAGGERHEART.GENERAL.actorName"}}' /> diff --git a/templates/ui/chat/parts/target-part.hbs b/templates/ui/chat/parts/target-part.hbs index 82c71456..af7e93b0 100644 --- a/templates/ui/chat/parts/target-part.hbs +++ b/templates/ui/chat/parts/target-part.hbs @@ -1,11 +1,11 @@
-
Target
+
{{pluralize currentTargets.length "DAGGERHEART.GENERAL.Target"}}
{{#if (or (and targets.length (or (gt targetShort.hit 0) (gt targetShort.miss 0))) (and hasSave pendingSaves))}}
{{#if (or (gt targetShort.hit 0) (gt targetShort.miss 0))}} -
{{targetShort.hit}} {{#if (gt targetShort.hit 1)}}{{localize "DAGGERHEART.GENERAL.hit.single"}}{{else}}{{localize "DAGGERHEART.GENERAL.hit.plural"}}{{/if}}
-
{{targetShort.miss}} {{#if (gt targetShort.miss 1)}}{{localize "DAGGERHEART.GENERAL.miss.single"}}{{else}}{{localize "DAGGERHEART.GENERAL.miss.plural"}}{{/if}}
+
{{targetShort.hit}} {{pluralize targetShort.hit "DAGGERHEART.GENERAL.hit"}}
+
{{targetShort.miss}} {{pluralize targetShort.miss "DAGGERHEART.GENERAL.miss"}}
{{/if}} {{#if (and hasSave pendingSaves)}}
{{/if}}
diff --git a/templates/ui/itemBrowser/itemBrowser.hbs b/templates/ui/itemBrowser/itemBrowser.hbs index 000e4c03..ca0def19 100644 --- a/templates/ui/itemBrowser/itemBrowser.hbs +++ b/templates/ui/itemBrowser/itemBrowser.hbs @@ -17,12 +17,12 @@
- +
{{#if fieldFilter.length}} - + {{/if}} - +
@@ -55,9 +55,9 @@ {{#if menu.data.columns.length}}
-
Name
+
{{localize 'DAGGERHEART.UI.ItemBrowser.columnName'}}
{{#each menu.data.columns}} - {{label}} + {{localize label}} {{/each}}
{{/if}} @@ -82,8 +82,8 @@ {{!--
--}} {{else}}
-

Daggerheart Compendium Browser

- Select a Folder in sidebar to start browsing trought the compendium +

{{localize "DAGGERHEART.UI.ItemBrowser.title"}}

+ {{localize "DAGGERHEART.UI.ItemBrowser.hint"}}
{{/if}}
\ No newline at end of file