[Community PR] Localize hardcoded text (#1002)

* Localize remaining hardcoded user-facing strings

* Introduce pluralize helper for localizing strings

* Localize missing strings from ItemBrowser
This commit is contained in:
Luiz HD Costa 2025-08-21 22:35:36 -03:00 committed by GitHub
parent e9f7c0c16b
commit 888cf9172b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 147 additions and 82 deletions

View file

@ -2347,6 +2347,42 @@
"playerMessage": "{user} rerolled their {name}" "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": { "Notifications": {
"adversaryMissing": "The linked adversary doesn't exist in the world.", "adversaryMissing": "The linked adversary doesn't exist in the world.",
"beastformInapplicable": "A beastform can only be applied to a Character.", "beastformInapplicable": "A beastform can only be applied to a Character.",
@ -2406,7 +2442,8 @@
"beastformEquipWeapon": "You cannot use weapons while in a Beastform.", "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.", "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.", "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", "multiclassAlreadyPresent": "You already have a class and multiclass",
"subclassesAlreadyPresent": "You already have a class and multiclass subclass", "subclassesAlreadyPresent": "You already have a class and multiclass subclass",
"noDiceSystem": "Your selected dice {system} does not have a {faces} dice" "noDiceSystem": "Your selected dice {system} does not have a {faces} dice"

View file

@ -154,7 +154,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
Object.values(config).forEach(c => { Object.values(config).forEach(c => {
const folder = { const folder = {
id: c.id, id: c.id,
label: c.label, label: game.i18n.localize(c.label),
selected: (!parent || parent.selected) && this.selectedMenu.path[depth] === c.id selected: (!parent || parent.selected) && this.selectedMenu.path[depth] === c.id
}; };
folder.folders = c.folders folder.folders = c.folders
@ -173,11 +173,16 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
folderPath = `${compendium}.folders.${folderId}`, folderPath = `${compendium}.folders.${folderId}`,
folderData = foundry.utils.getProperty(config, folderPath); folderData = foundry.utils.getProperty(config, folderPath);
const columns = ItemBrowser.getFolderConfig(folderData).map(col => ({
...col,
label: game.i18n.localize(col.label)
}));
this.selectedMenu = { this.selectedMenu = {
path: folderPath.split('.'), path: folderPath.split('.'),
data: { data: {
...folderData, ...folderData,
columns: ItemBrowser.getFolderConfig(folderData) columns: columns
} }
}; };
@ -237,6 +242,12 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
else if (typeof f.choices === 'function') { else if (typeof f.choices === 'function') {
f.choices = f.choices(); 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.name ??= f.key;
f.value = this.presets?.filter?.[f.name]?.value ?? null; f.value = this.presets?.filter?.[f.name]?.value ?? null;
}); });

View file

@ -3,63 +3,63 @@ export const typeConfig = {
columns: [ columns: [
{ {
key: "system.tier", key: "system.tier",
label: "Tier" label: "DAGGERHEART.GENERAL.Tiers.singular"
}, },
{ {
key: "system.type", key: "system.type",
label: "Type" label: "DAGGERHEART.GENERAL.type"
} }
], ],
filters: [ filters: [
{ {
key: "system.tier", key: "system.tier",
label: "Tier", label: "DAGGERHEART.GENERAL.Tiers.singular",
field: 'system.api.models.actors.DhAdversary.schema.fields.tier' field: 'system.api.models.actors.DhAdversary.schema.fields.tier'
}, },
{ {
key: "system.type", key: "system.type",
label: "Type", label: "DAGGERHEART.GENERAL.type",
field: 'system.api.models.actors.DhAdversary.schema.fields.type' field: 'system.api.models.actors.DhAdversary.schema.fields.type'
}, },
{ {
key: "system.difficulty", key: "system.difficulty",
name: "difficulty.min", name: "difficulty.min",
label: "Difficulty (Min)", label: "DAGGERHEART.UI.ItemBrowser.difficultyMin",
field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty', field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty',
operator: "gte" operator: "gte"
}, },
{ {
key: "system.difficulty", key: "system.difficulty",
name: "difficulty.max", name: "difficulty.max",
label: "Difficulty (Max)", label: "DAGGERHEART.UI.ItemBrowser.difficultyMax",
field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty', field: 'system.api.models.actors.DhAdversary.schema.fields.difficulty',
operator: "lte" operator: "lte"
}, },
{ {
key: "system.resources.hitPoints.max", key: "system.resources.hitPoints.max",
name: "hp.min", 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', field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max',
operator: "gte" operator: "gte"
}, },
{ {
key: "system.resources.hitPoints.max", key: "system.resources.hitPoints.max",
name: "hp.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', field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.hitPoints.fields.max',
operator: "lte" operator: "lte"
}, },
{ {
key: "system.resources.stress.max", key: "system.resources.stress.max",
name: "stress.min", name: "stress.min",
label: "Stress (Min)", label: "DAGGERHEART.UI.ItemBrowser.stressMin",
field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max', field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max',
operator: "gte" operator: "gte"
}, },
{ {
key: "system.resources.stress.max", key: "system.resources.stress.max",
name: "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', field: 'system.api.models.actors.DhAdversary.schema.fields.resources.fields.stress.fields.max',
operator: "lte" operator: "lte"
}, },
@ -69,70 +69,70 @@ export const typeConfig = {
columns: [ columns: [
{ {
key: "type", key: "type",
label: "Type" label: "DAGGERHEART.GENERAL.type"
}, },
{ {
key: "system.secondary", key: "system.secondary",
label: "Subtype", label: "DAGGERHEART.UI.ItemBrowser.subtype",
format: (isSecondary) => isSecondary ? "secondary" : (isSecondary === false ? "primary" : '-') format: (isSecondary) => isSecondary ? "secondary" : (isSecondary === false ? "primary" : '-')
}, },
{ {
key: "system.tier", key: "system.tier",
label: "Tier" label: "DAGGERHEART.GENERAL.Tiers.singular"
} }
], ],
filters: [ filters: [
{ {
key: "type", 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 })) choices: () => CONFIG.Item.documentClass.TYPES.filter(t => ["armor", "weapon", "consumable", "loot"].includes(t)).map(t => ({ value: t, label: t }))
}, },
{ {
key: "system.secondary", key: "system.secondary",
label: "Subtype", label: "DAGGERHEART.UI.ItemBrowser.subtype",
choices: [ choices: [
{ value: false, label: "Primary Weapon"}, { value: false, label: "DAGGERHEART.ITEMS.Weapon.primaryWeapon" },
{ value: true, label: "Secondary Weapon"} { value: true, label: "DAGGERHEART.ITEMS.Weapon.secondaryWeapon" }
] ]
}, },
{ {
key: "system.tier", key: "system.tier",
label: "Tier", label: "DAGGERHEART.GENERAL.Tiers.singular",
choices: [{ value: "1", label: "1"}, { value: "2", label: "2"}, { value: "3", label: "3"}, { value: "4", label: "4"}] choices: [{ value: "1", label: "1" }, { value: "2", label: "2" }, { value: "3", label: "3" }, { value: "4", label: "4" }]
}, },
{ {
key: "system.burden", key: "system.burden",
label: "Burden", label: "DAGGERHEART.GENERAL.burden",
field: 'system.api.models.items.DHWeapon.schema.fields.burden' field: 'system.api.models.items.DHWeapon.schema.fields.burden'
}, },
{ {
key: "system.attack.roll.trait", key: "system.attack.roll.trait",
label: "Trait", label: "DAGGERHEART.GENERAL.Trait.single",
field: 'system.api.models.actions.actionsTypes.attack.schema.fields.roll.fields.trait' field: 'system.api.models.actions.actionsTypes.attack.schema.fields.roll.fields.trait'
}, },
{ {
key: "system.attack.range", key: "system.attack.range",
label: "Range", label: "DAGGERHEART.GENERAL.range",
field: 'system.api.models.actions.actionsTypes.attack.schema.fields.range' field: 'system.api.models.actions.actionsTypes.attack.schema.fields.range'
}, },
{ {
key: "system.baseScore", key: "system.baseScore",
name: "armor.min", name: "armor.min",
label: "Armor Score (Min)", label: "DAGGERHEART.UI.ItemBrowser.armorScoreMin",
field: 'system.api.models.items.DHArmor.schema.fields.baseScore', field: 'system.api.models.items.DHArmor.schema.fields.baseScore',
operator: "gte" operator: "gte"
}, },
{ {
key: "system.baseScore", key: "system.baseScore",
name: "armor.max", name: "armor.max",
label: "Armor Score (Max)", label: "DAGGERHEART.UI.ItemBrowser.armorScoreMax",
field: 'system.api.models.items.DHArmor.schema.fields.baseScore', field: 'system.api.models.items.DHArmor.schema.fields.baseScore',
operator: "lte" operator: "lte"
}, },
{ {
key: "system.itemFeatures", key: "system.itemFeatures",
label: "Features", 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})), choices: () => [...Object.entries(CONFIG.DH.ITEM.weaponFeatures), ...Object.entries(CONFIG.DH.ITEM.armorFeatures)].map(([k, v]) => ({ value: k, label: v.label })),
operator: "contains3" operator: "contains3"
} }
] ]
@ -149,54 +149,54 @@ export const typeConfig = {
columns: [ columns: [
{ {
key: "system.type", key: "system.type",
label: "Type" label: "DAGGERHEART.GENERAL.type"
}, },
{ {
key: "system.domain", key: "system.domain",
label: "Domain" label: "DAGGERHEART.GENERAL.Domain.single"
}, },
{ {
key: "system.level", key: "system.level",
label: "Level" label: "DAGGERHEART.GENERAL.level"
} }
], ],
filters: [ filters: [
{ {
key: "system.type", key: "system.type",
label: "Type", label: "DAGGERHEART.GENERAL.type",
field: 'system.api.models.items.DHDomainCard.schema.fields.type' field: 'system.api.models.items.DHDomainCard.schema.fields.type'
}, },
{ {
key: "system.domain", key: "system.domain",
label: "Domain", label: "DAGGERHEART.GENERAL.Domain.single",
field: 'system.api.models.items.DHDomainCard.schema.fields.domain', field: 'system.api.models.items.DHDomainCard.schema.fields.domain',
operator: "contains2" operator: "contains2"
}, },
{ {
key: "system.level", key: "system.level",
name: "level.min", name: "level.min",
label: "Level (Min)", label: "DAGGERHEART.UI.ItemBrowser.levelMin",
field: 'system.api.models.items.DHDomainCard.schema.fields.level', field: 'system.api.models.items.DHDomainCard.schema.fields.level',
operator: "gte" operator: "gte"
}, },
{ {
key: "system.level", key: "system.level",
name: "level.max", name: "level.max",
label: "Level (Max)", label: "DAGGERHEART.UI.ItemBrowser.levelMax",
field: 'system.api.models.items.DHDomainCard.schema.fields.level', field: 'system.api.models.items.DHDomainCard.schema.fields.level',
operator: "lte" operator: "lte"
}, },
{ {
key: "system.recallCost", key: "system.recallCost",
name: "recall.min", name: "recall.min",
label: "Recall Cost (Min)", label: "DAGGERHEART.UI.ItemBrowser.recallCostMin",
field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost', field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost',
operator: "gte" operator: "gte"
}, },
{ {
key: "system.recallCost", key: "system.recallCost",
name: "recall.max", name: "recall.max",
label: "Recall Cost (Max)", label: "DAGGERHEART.UI.ItemBrowser.recallCostMax",
field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost', field: 'system.api.models.items.DHDomainCard.schema.fields.recallCost',
operator: "lte" operator: "lte"
} }
@ -206,50 +206,50 @@ export const typeConfig = {
columns: [ columns: [
{ {
key: "system.evasion", key: "system.evasion",
label: "Evasion" label: "DAGGERHEART.GENERAL.evasion"
}, },
{ {
key: "system.hitPoints", key: "system.hitPoints",
label: "Hit Points" label: "DAGGERHEART.GENERAL.HitPoints.plural"
}, },
{ {
key: "system.domains", key: "system.domains",
label: "Domains" label: "DAGGERHEART.GENERAL.Domain.plural"
} }
], ],
filters: [ filters: [
{ {
key: "system.evasion", key: "system.evasion",
name: "evasion.min", name: "evasion.min",
label: "Evasion (Min)", label: "DAGGERHEART.UI.ItemBrowser.evasionMin",
field: 'system.api.models.items.DHClass.schema.fields.evasion', field: 'system.api.models.items.DHClass.schema.fields.evasion',
operator: "gte" operator: "gte"
}, },
{ {
key: "system.evasion", key: "system.evasion",
name: "evasion.max", name: "evasion.max",
label: "Evasion (Max)", label: "DAGGERHEART.UI.ItemBrowser.evasionMax",
field: 'system.api.models.items.DHClass.schema.fields.evasion', field: 'system.api.models.items.DHClass.schema.fields.evasion',
operator: "lte" operator: "lte"
}, },
{ {
key: "system.hitPoints", key: "system.hitPoints",
name: "hp.min", name: "hp.min",
label: "Hit Points (Min)", label: "DAGGERHEART.UI.ItemBrowser.hitPointsMin",
field: 'system.api.models.items.DHClass.schema.fields.hitPoints', field: 'system.api.models.items.DHClass.schema.fields.hitPoints',
operator: "gte" operator: "gte"
}, },
{ {
key: "system.hitPoints", key: "system.hitPoints",
name: "hp.max", name: "hp.max",
label: "Hit Points (Max)", label: "DAGGERHEART.UI.ItemBrowser.hitPointsMax",
field: 'system.api.models.items.DHClass.schema.fields.hitPoints', field: 'system.api.models.items.DHClass.schema.fields.hitPoints',
operator: "lte" operator: "lte"
}, },
{ {
key: "system.domains", key: "system.domains",
label: "Domains", label: "DAGGERHEART.GENERAL.Domain.plural",
choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label})), choices: () => Object.values(CONFIG.DH.DOMAIN.domains).map(d => ({ value: d.id, label: d.label })),
operator: "contains2" operator: "contains2"
} }
] ]
@ -258,14 +258,14 @@ export const typeConfig = {
columns: [ columns: [
{ {
key: "id", key: "id",
label: "Class", label: "TYPES.Item.class",
format: (id) => { format: (id) => {
return ""; return "";
} }
}, },
{ {
key: "system.spellcastingTrait", key: "system.spellcastingTrait",
label: "Spellcasting Trait" label: "DAGGERHEART.ITEMS.Subclass.spellcastingTrait"
} }
], ],
filters: [] filters: []
@ -274,22 +274,22 @@ export const typeConfig = {
columns: [ columns: [
{ {
key: "system.tier", key: "system.tier",
label: "Tier" label: "DAGGERHEART.GENERAL.Tiers.singular"
}, },
{ {
key: "system.mainTrait", key: "system.mainTrait",
label: "Main Trait" label: "DAGGERHEART.GENERAL.Trait.single"
} }
], ],
filters: [ filters: [
{ {
key: "system.tier", key: "system.tier",
label: "Tier", label: "DAGGERHEART.GENERAL.Tiers.singular",
field: 'system.api.models.items.DHBeastform.schema.fields.tier' field: 'system.api.models.items.DHBeastform.schema.fields.tier'
}, },
{ {
key: "system.mainTrait", key: "system.mainTrait",
label: "Main Trait", label: "DAGGERHEART.GENERAL.Trait.single",
field: 'system.api.models.items.DHBeastform.schema.fields.mainTrait' field: 'system.api.models.items.DHBeastform.schema.fields.mainTrait'
} }
] ]
@ -304,20 +304,20 @@ export const compendiumConfig = {
"adversaries": { "adversaries": {
id: "adversaries", id: "adversaries",
keys: ["adversaries"], keys: ["adversaries"],
label: "Adversaries", label: "DAGGERHEART.UI.ItemBrowser.folders.adversaries",
type: ["adversary"], type: ["adversary"],
listType: "adversaries" listType: "adversaries"
}, },
"ancestries": { "ancestries": {
id: "ancestries", id: "ancestries",
keys: ["ancestries"], keys: ["ancestries"],
label: "Ancestries", label: "DAGGERHEART.UI.ItemBrowser.folders.ancestries",
type: ["ancestry"], type: ["ancestry"],
folders: { folders: {
"features": { "features": {
id: "features", id: "features",
keys: ["ancestries"], keys: ["ancestries"],
label: "Features", label: "DAGGERHEART.UI.ItemBrowser.folders.features",
type: ["feature"] type: ["feature"]
} }
} }
@ -325,26 +325,26 @@ export const compendiumConfig = {
"equipments": { "equipments": {
id: "equipments", id: "equipments",
keys: ["armors", "weapons", "consumables", "loot"], keys: ["armors", "weapons", "consumables", "loot"],
label: "Equipment", label: "DAGGERHEART.UI.ItemBrowser.folders.equipment",
type: ["armor", "weapon", "consumable", "loot"], type: ["armor", "weapon", "consumable", "loot"],
listType: "items" listType: "items"
}, },
"classes": { "classes": {
id: "classes", id: "classes",
keys: ["classes"], keys: ["classes"],
label: "Classes", label: "DAGGERHEART.UI.ItemBrowser.folders.classes",
type: ["class"], type: ["class"],
folders: { folders: {
"features": { "features": {
id: "features", id: "features",
keys: ["classes"], keys: ["classes"],
label: "Features", label: "DAGGERHEART.UI.ItemBrowser.folders.features",
type: ["feature"] type: ["feature"]
}, },
"items": { "items": {
id: "items", id: "items",
keys: ["classes"], keys: ["classes"],
label: "Items", label: "DAGGERHEART.UI.ItemBrowser.folders.items",
type: ["armor", "weapon", "consumable", "loot"], type: ["armor", "weapon", "consumable", "loot"],
listType: "items" listType: "items"
} }
@ -354,27 +354,27 @@ export const compendiumConfig = {
"subclasses": { "subclasses": {
id: "subclasses", id: "subclasses",
keys: ["subclasses"], keys: ["subclasses"],
label: "Subclasses", label: "DAGGERHEART.UI.ItemBrowser.folders.subclasses",
type: ["subclass"], type: ["subclass"],
listType: "subclasses" listType: "subclasses"
}, },
"domains": { "domains": {
id: "domains", id: "domains",
keys: ["domains"], keys: ["domains"],
label: "Domain Cards", label: "DAGGERHEART.UI.ItemBrowser.folders.domainCards",
type: ["domainCard"], type: ["domainCard"],
listType: "cards" listType: "cards"
}, },
"communities": { "communities": {
id: "communities", id: "communities",
keys: ["communities"], keys: ["communities"],
label: "Communities", label: "DAGGERHEART.UI.ItemBrowser.folders.communities",
type: ["community"], type: ["community"],
folders: { folders: {
"features": { "features": {
id: "features", id: "features",
keys: ["communities"], keys: ["communities"],
label: "Features", label: "DAGGERHEART.UI.ItemBrowser.folders.features",
type: ["feature"] type: ["feature"]
} }
} }
@ -382,20 +382,20 @@ export const compendiumConfig = {
"environments": { "environments": {
id: "environments", id: "environments",
keys: ["environments"], keys: ["environments"],
label: "Environments", label: "DAGGERHEART.UI.ItemBrowser.folders.environments",
type: ["environment"] type: ["environment"]
}, },
"beastforms": { "beastforms": {
id: "beastforms", id: "beastforms",
keys: ["beastforms"], keys: ["beastforms"],
label: "Beastforms", label: "DAGGERHEART.UI.ItemBrowser.folders.beastforms",
type: ["beastform"], type: ["beastform"],
listType: "beastforms", listType: "beastforms",
folders: { folders: {
"features": { "features": {
id: "features", id: "features",
keys: ["beastforms"], keys: ["beastforms"],
label: "Features", label: "DAGGERHEART.UI.ItemBrowser.folders.features",
type: ["feature"] type: ["feature"]
} }
} }

View file

@ -25,7 +25,7 @@ export default class CostField extends fields.ArrayField {
config.costs = CostField.calcCosts.call(this, costs); config.costs = CostField.calcCosts.call(this, costs);
const hasCost = CostField.hasCost.call(this, config.costs); const hasCost = CostField.hasCost.call(this, config.costs);
if (config.isFastForward && !hasCost) 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; return hasCost;
} }

View file

@ -25,7 +25,7 @@ export default class UsesField extends fields.SchemaField {
if (uses && !uses.value) uses.value = 0; if (uses && !uses.value) uses.value = 0;
config.uses = uses; config.uses = uses;
const hasUses = UsesField.hasUses.call(this, config.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; return hasUses;
} }

View file

@ -13,7 +13,8 @@ export default class RegisterHandlebarsHelpers {
hasProperty: foundry.utils.hasProperty, hasProperty: foundry.utils.hasProperty,
getProperty: foundry.utils.getProperty, getProperty: foundry.utils.getProperty,
setVar: this.setVar, setVar: this.setVar,
empty: this.empty empty: this.empty,
pluralize: this.pluralize
}); });
} }
static add(a, b) { static add(a, b) {
@ -64,7 +65,7 @@ export default class RegisterHandlebarsHelpers {
return isNumerical ? (!result ? 0 : Number(result)) : result; return isNumerical ? (!result ? 0 : Number(result)) : result;
} }
static setVar(name, value, context) { static setVar(name, value) {
this[name] = value; this[name] = value;
} }
@ -72,4 +73,20 @@ export default class RegisterHandlebarsHelpers {
if (!(typeof object === 'object')) return true; if (!(typeof object === 'object')) return true;
return Object.keys(object).length === 0; 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);
}
} }

View file

@ -6,7 +6,7 @@
type='text' type='text'
name='name' name='name'
value='{{document.name}}' value='{{document.name}}'
placeholder='Actor Name' placeholder='{{localize "DAGGERHEART.GENERAL.actorName"}}'
/> />
</h1> </h1>

View file

@ -1,11 +1,11 @@
<div class="roll-part target-section dice-roll" data-action="expandRoll"> <div class="roll-part target-section dice-roll" data-action="expandRoll">
<div class="roll-part-header"><div><span>Target</span></div></div> <div class="roll-part-header"><div><span>{{pluralize currentTargets.length "DAGGERHEART.GENERAL.Target"}}</span></div></div>
{{#if (or (and targets.length (or (gt targetShort.hit 0) (gt targetShort.miss 0))) (and hasSave pendingSaves))}} {{#if (or (and targets.length (or (gt targetShort.hit 0) (gt targetShort.miss 0))) (and hasSave pendingSaves))}}
<div class="roll-part-extra on-reduced"> <div class="roll-part-extra on-reduced">
<div class="wrapper"> <div class="wrapper">
{{#if (or (gt targetShort.hit 0) (gt targetShort.miss 0))}} {{#if (or (gt targetShort.hit 0) (gt targetShort.miss 0))}}
<div class="target-hit-status">{{targetShort.hit}} {{#if (gt targetShort.hit 1)}}{{localize "DAGGERHEART.GENERAL.hit.single"}}{{else}}{{localize "DAGGERHEART.GENERAL.hit.plural"}}{{/if}}</div> <div class="target-hit-status">{{targetShort.hit}} {{pluralize targetShort.hit "DAGGERHEART.GENERAL.hit"}}</div>
<div class="target-hit-status is-miss">{{targetShort.miss}} {{#if (gt targetShort.miss 1)}}{{localize "DAGGERHEART.GENERAL.miss.single"}}{{else}}{{localize "DAGGERHEART.GENERAL.miss.plural"}}{{/if}}</div> <div class="target-hit-status is-miss">{{targetShort.miss}} {{pluralize targetShort.miss "DAGGERHEART.GENERAL.miss"}}</div>
{{/if}} {{/if}}
{{#if (and hasSave pendingSaves)}}<div class="target-pending-saves" data-tooltip="{{localize "DAGGERHEART.UI.Tooltip.pendingSaves"}}" data-tooltip-direction="UP"><i class="fa-solid fa-shield fa-lg fa-beat"></i></div>{{/if}} {{#if (and hasSave pendingSaves)}}<div class="target-pending-saves" data-tooltip="{{localize "DAGGERHEART.UI.Tooltip.pendingSaves"}}" data-tooltip-direction="UP"><i class="fa-solid fa-shield fa-lg fa-beat"></i></div>{{/if}}
</div> </div>

View file

@ -17,12 +17,12 @@
<div class="icon"> <div class="icon">
<i class="fa-solid fa-magnifying-glass"></i> <i class="fa-solid fa-magnifying-glass"></i>
</div> </div>
<input type="search" name="search" class="search-input" placeholder="Search..."> <input type="search" name="search" class="search-input" placeholder="{{localize 'DAGGERHEART.UI.ItemBrowser.searchPlaceholder'}}">
</div> </div>
{{#if fieldFilter.length}} {{#if fieldFilter.length}}
<a data-tooltip="Filters" data-action="expandContent"><i class="fa-solid fa-filter"></i></a> <a data-tooltip="{{localize 'DAGGERHEART.UI.ItemBrowser.tooltipFilters'}}" data-action="expandContent"><i class="fa-solid fa-filter"></i></a>
{{/if}} {{/if}}
<a data-tooltip="Erase" data-action="resetFilters"><i class="fa-solid fa-eraser"></i></a> <a data-tooltip="{{localize 'DAGGERHEART.UI.ItemBrowser.tooltipErase'}}" data-action="resetFilters"><i class="fa-solid fa-eraser"></i></a>
</div> </div>
<div class="filter-content extensible"> <div class="filter-content extensible">
<div class="wrapper"> <div class="wrapper">
@ -55,9 +55,9 @@
{{#if menu.data.columns.length}} {{#if menu.data.columns.length}}
<div class="item-list-header"> <div class="item-list-header">
<div class="item-list-img"></div> <div class="item-list-img"></div>
<div class="item-list-name" data-sort-key="name" data-sort-type="ASC" data-action="sortList">Name</div> <div class="item-list-name" data-sort-key="name" data-sort-type="ASC" data-action="sortList">{{localize 'DAGGERHEART.UI.ItemBrowser.columnName'}}</div>
{{#each menu.data.columns}} {{#each menu.data.columns}}
<span data-sort-key="{{key}}" data-sort-type="" data-action="sortList">{{label}}</span> <span data-sort-key="{{key}}" data-sort-type="" data-action="sortList">{{localize label}}</span>
{{/each}} {{/each}}
</div> </div>
{{/if}} {{/if}}
@ -82,8 +82,8 @@
{{!-- </div> --}} {{!-- </div> --}}
{{else}} {{else}}
<div class="welcome-message"> <div class="welcome-message">
<h2 class="title">Daggerheart Compendium Browser</h2> <h2 class="title">{{localize "DAGGERHEART.UI.ItemBrowser.title"}}</h2>
<span class="hint"><i>Select a Folder in sidebar to start browsing trought the compendium</i></span> <span class="hint"><i>{{localize "DAGGERHEART.UI.ItemBrowser.hint"}}</i></span>
</div> </div>
{{/if}} {{/if}}
</div> </div>