1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2026-03-02 03:39:21 +00:00

Initial support for translations

This commit is contained in:
tobspr
2020-05-17 12:12:13 +02:00
parent 13c6fc7598
commit a70a937302
44 changed files with 573 additions and 328 deletions

View File

@@ -39,6 +39,13 @@
.hotkey {
color: lighten($colorGreenBright, 10);
font-weight: bold;
display: flex;
flex-direction: row;
align-items: center;
.keybinding {
position: relative;
@include S(margin-left, 5px);
}
}
.buildingImage {

View File

@@ -66,17 +66,13 @@
}
}
.keybinding.shift {
.keybinding.builtinKey {
transition: all 0.1s ease-in-out;
transition-property: background-color, color, border-color;
background: $colorRedBright;
border-color: $colorRedBright;
color: #fff;
}
&.shiftDown .keybinding.shift {
border-color: darken($colorRedBright, 40);
}
}
body.uiHidden #ingame_HUD_KeybindingOverlay .binding:not(.hudToggle) {

View File

@@ -251,9 +251,9 @@
@include S(padding, 15px);
> a {
display: flex;
flex-direction: row;
display: grid;
align-items: center;
grid-template-columns: 1fr auto;
justify-content: center;
background: #fafafa;
@@ -280,8 +280,8 @@
.thirdpartyLogo {
display: inline-block;
width: 80%;
height: 80%;
@include S(width, 50px);
@include S(height, 50px);
background: center center / 80% no-repeat;
&.githubLogo {
background-image: uiResource("main_menu/github.png");

1
src/js/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
translations-built

View File

@@ -76,7 +76,7 @@ export const globalConfig = {
debug: {
/* dev:start */
// fastGameEnter: true,
fastGameEnter: true,
noArtificialDelays: true,
// disableSavegameWrite: true,
showEntityBounds: false,
@@ -90,6 +90,7 @@ export const globalConfig = {
allBuildingsUnlocked: true,
upgradesNoCost: true,
disableUnlockDialog: false,
testTranslations: true,
/* dev:end */
},

View File

@@ -11,6 +11,7 @@ import { FormElement } from "./modal_dialog_forms";
import { globalConfig } from "./config";
import { getStringForKeyCode } from "../game/key_action_mapper";
import { createLogger } from "./logging";
import { T } from "../translations";
const kbEnter = 13;
const kbCancel = 27;
@@ -146,8 +147,7 @@ export class Dialog {
button.classList.add("button");
button.classList.add("styledButton");
button.classList.add(buttonStyle);
// button.innerText = T.dialog_buttons[buttonId];
button.innerText = buttonId;
button.innerText = T.dialogs.buttons[buttonId];
const params = (rawParams || "").split("/");
const useTimeout = params.indexOf("timeout") >= 0;
@@ -277,7 +277,7 @@ export class DialogLoading extends Dialog {
const loader = document.createElement("div");
loader.classList.add("prefab_LoadingTextWithAnim");
loader.classList.add("loadingIndicator");
loader.innerText = "Loading";
loader.innerText = T.global.loading;
elem.appendChild(loader);
this.app.inputMgr.pushReciever(this.inputReciever);

View File

@@ -15,6 +15,7 @@ import {
performanceNow,
} from "./builtins";
import { Vector } from "./vector";
import { T } from "../translations";
// Constants
export const TOP = new Vector(0, -1);
@@ -421,7 +422,7 @@ export function formatBigNumber(num, divider = ".") {
num = Math_abs(num);
if (num > 1e54) {
return sign + "inf";
return sign + T.global.infinite;
}
if (num < 10 && !Number.isInteger(num)) {
@@ -459,7 +460,7 @@ export function formatBigNumberFull(num, divider = ",") {
return num + "";
}
if (num > 1e54) {
return "infinite";
return T.global.infinite;
}
let rest = num;
let out = "";
@@ -831,24 +832,47 @@ export function formatSecondsToTimeAgo(secs) {
if (seconds <= 60) {
if (seconds <= 1) {
return "one second ago";
return T.global.time.oneSecondAgo;
}
return seconds + " seconds ago";
return T.global.time.xSecondsAgo.replace("<x>", "" + seconds);
} else if (minutes <= 60) {
if (minutes <= 1) {
return "one minute ago";
return T.global.time.oneMinuteAgo;
}
return minutes + " minutes ago";
return T.global.time.xMinutesAgo.replace("<x>", "" + minutes);
} else if (hours <= 60) {
if (hours <= 1) {
return "one hour ago";
return T.global.time.oneHourAgo;
}
return hours + " hours ago";
return T.global.time.xHoursAgo.replace("<x>", "" + hours);
} else {
if (days <= 1) {
return "one day ago";
return T.global.time.oneDayAgo;
}
return days + " days ago";
return T.global.time.xDaysAgo.replace("<x>", "" + days);
}
}
/**
* Formats seconds into a readable string like "5h 23m"
* @param {number} secs Seconds
* @returns {string}
*/
export function formatSeconds(secs) {
const trans = T.global.time;
secs = Math_ceil(secs);
if (secs < 60) {
return trans.secondsShort.replace("<seconds>", "" + secs);
} else if (secs < 60 * 60) {
const minutes = Math_floor(secs / 60);
const seconds = secs % 60;
return trans.minutesAndSecondsShort
.replace("<seconds>", "" + seconds)
.replace("<minutes>", "" + minutes);
} else {
const hours = Math_floor(secs / 3600);
const minutes = Math_floor(secs / 60) % 60;
return trans.hoursAndMinutesShort.replace("<minutes>", "" + minutes).replace("<hours>", "" + hours);
}
}
@@ -868,3 +892,11 @@ export function generateFileDownload(filename, text) {
element.click();
document.body.removeChild(element);
}
/**
* Capitalizes the first letter
* @param {string} str
*/
export function capitalizeFirstLetter(str) {
return str.substr(0, 1).toUpperCase() + str.substr(1).toLowerCase();
}

View File

@@ -20,14 +20,6 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
return "#777";
}
getName() {
return "Belt";
}
getDescription() {
return "Transports items, hold and drag to place multiple.";
}
getPreviewSprite(rotationVariant) {
switch (arrayBeltVariantToRotation[rotationVariant]) {
case enumDirection.top: {

View File

@@ -30,14 +30,6 @@ export class MetaCutterBuilding extends MetaBuilding {
}
}
getName() {
return "Cut Half";
}
getDescription() {
return "Cuts shapes from top to bottom and outputs both halfs. <strong>If you use only one part, be sure to destroy the other part or it will stall!</strong>";
}
getAvailableVariants(root) {
return [defaultBuildingVariant, enumCutterVariants.quad];
}

View File

@@ -20,14 +20,6 @@ export class MetaHubBuilding extends MetaBuilding {
return "#eb5555";
}
getName() {
return "Hub";
}
getDescription() {
return "Your central hub, deliver shapes to it to unlock new buildings.";
}
isRotateable() {
return false;
}

View File

@@ -13,18 +13,10 @@ export class MetaMinerBuilding extends MetaBuilding {
super("miner");
}
getName() {
return "Extract";
}
getSilhouetteColor() {
return "#b37dcd";
}
getDescription() {
return "Place over a shape or color to extract it. Six extractors fill exactly one belt.";
}
getAvailableVariants(root) {
return [defaultBuildingVariant, enumMinerVariants.chainable];
}

View File

@@ -17,14 +17,6 @@ export class MetaMixerBuilding extends MetaBuilding {
return new Vector(2, 1);
}
getName() {
return "Mix Colors";
}
getDescription() {
return "Mixes two colors using additive blending.";
}
getSilhouetteColor() {
return "#cdbb7d";
}

View File

@@ -29,14 +29,6 @@ export class MetaPainterBuilding extends MetaBuilding {
}
}
getName() {
return "Dye";
}
getDescription() {
return "Colors the whole shape on the left input with the color from the right input.";
}
getSilhouetteColor() {
return "#cd9b7d";
}

View File

@@ -16,14 +16,6 @@ export class MetaRotaterBuilding extends MetaBuilding {
super("rotater");
}
getName() {
return "Rotate";
}
getDescription() {
return "Rotates shapes clockwise by 90 degrees.";
}
getSilhouetteColor() {
return "#7dc6cd";
}

View File

@@ -27,18 +27,10 @@ export class MetaSplitterBuilding extends MetaBuilding {
}
}
getName() {
return "Balancer";
}
getSilhouetteColor() {
return "#444";
}
getDescription() {
return "Multifunctional - Evenly distributes all inputs onto all outputs.";
}
getAvailableVariants(root) {
return [defaultBuildingVariant, enumSplitterVariants.compact];
}

View File

@@ -13,18 +13,10 @@ export class MetaStackerBuilding extends MetaBuilding {
super("stacker");
}
getName() {
return "Combine";
}
getSilhouetteColor() {
return "#9fcd7d";
}
getDescription() {
return "Combines both items. If they can not be merged, the right item is placed above the left item.";
}
getDimensions() {
return new Vector(2, 1);
}

View File

@@ -12,14 +12,6 @@ export class MetaTrashBuilding extends MetaBuilding {
super("trash");
}
getName() {
return "Destroyer";
}
getDescription() {
return "Accepts inputs from all sides and destroys them. Forever.";
}
isRotateable() {
return false;
}

View File

@@ -28,18 +28,10 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
super("underground_belt");
}
getName() {
return "Tunnel";
}
getSilhouetteColor() {
return "#555";
}
getDescription() {
return "Allows to tunnel resources under buildings and belts.";
}
getFlipOrientationAfterPlacement() {
return true;
}

View File

@@ -1,6 +1,7 @@
/* typehints:start */
import { Application } from "../application";
/* typehints:end */
import { T } from "../translations";
export class GameLoadingOverlay {
/**
@@ -51,7 +52,7 @@ export class GameLoadingOverlay {
internalAddSpinnerAndText(element) {
const inner = document.createElement("span");
inner.classList.add("prefab_LoadingTextWithAnim");
inner.innerText = "Loading";
inner.innerText = T.global.loading;
element.appendChild(inner);
}
}

View File

@@ -1,23 +1,24 @@
import { BaseHUDPart } from "../base_hud_part";
import { MetaBuilding, defaultBuildingVariant } from "../../meta_building";
import { DrawParameters } from "../../../core/draw_parameters";
import { Math_abs, Math_degrees, Math_radians } from "../../../core/builtins";
import { globalConfig } from "../../../core/config";
import { StaticMapEntityComponent } from "../../components/static_map_entity";
import { STOP_PROPAGATION, Signal } from "../../../core/signal";
import {
Vector,
enumDirectionToAngle,
enumInvertedDirections,
enumDirectionToVector,
} from "../../../core/vector";
import { pulseAnimation, makeDiv, removeAllChildren } from "../../../core/utils";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { TrackedState } from "../../../core/tracked_state";
import { Math_abs, Math_radians, Math_degrees } from "../../../core/builtins";
import { Loader } from "../../../core/loader";
import { DrawParameters } from "../../../core/draw_parameters";
import { drawRotatedSprite } from "../../../core/draw_utils";
import { Entity } from "../../entity";
import { Loader } from "../../../core/loader";
import { STOP_PROPAGATION } from "../../../core/signal";
import { TrackedState } from "../../../core/tracked_state";
import { makeDiv, removeAllChildren } from "../../../core/utils";
import {
enumDirectionToAngle,
enumDirectionToVector,
enumInvertedDirections,
Vector,
} from "../../../core/vector";
import { enumMouseButton } from "../../camera";
import { StaticMapEntityComponent } from "../../components/static_map_entity";
import { Entity } from "../../entity";
import { defaultBuildingVariant, MetaBuilding } from "../../meta_building";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { T } from "../../../translations";
export class HUDBuildingPlacer extends BaseHUDPart {
initialize() {
@@ -231,13 +232,16 @@ export class HUDBuildingPlacer extends BaseHUDPart {
this.abortDragging();
this.root.hud.signals.selectedPlacementBuildingChanged.dispatch(metaBuilding);
if (metaBuilding) {
this.buildingInfoElements.label.innerHTML = metaBuilding.getName();
this.buildingInfoElements.descText.innerHTML = metaBuilding.getDescription();
this.buildingInfoElements.label.innerHTML = T.buildings[metaBuilding.id].name;
this.buildingInfoElements.descText.innerHTML = T.buildings[metaBuilding.id].description;
const binding = this.root.gameState.keyActionMapper.getBinding(
"building_" + metaBuilding.getId()
);
this.buildingInfoElements.hotkey.innerHTML = "Hotkey: " + binding.getKeyCodeString();
this.buildingInfoElements.hotkey.innerHTML = T.ingame.buildingPlacement.hotkeyLabel.replace(
"<key>",
"<code class='keybinding'>" + binding.getKeyCodeString() + "</code>"
);
const variant = this.preferredVariants[metaBuilding.getId()] || defaultBuildingVariant;
this.currentVariant.set(variant);
@@ -283,11 +287,12 @@ export class HUDBuildingPlacer extends BaseHUDPart {
this.variantsElement,
null,
["explanation"],
`
Press <code class='keybinding'>${this.root.gameState.keyActionMapper
.getBinding("cycle_variants")
.getKeyCodeString()}</code> to cycle variants.
`
T.ingame.buildingPlacement.cycleBuildingVariants.replace(
"<key>",
"<code class='keybinding'>" +
this.root.gameState.keyActionMapper.getBinding("cycle_variants").getKeyCodeString() +
"</code>"
)
);
for (let i = 0; i < availableVariants.length; ++i) {

View File

@@ -2,6 +2,7 @@ import { BaseHUDPart } from "../base_hud_part";
import { makeDiv, randomInt } from "../../../core/utils";
import { SOUNDS } from "../../../platform/sound";
import { enumNotificationType } from "./notifications";
import { T } from "../../../translations";
export class HUDGameMenu extends BaseHUDPart {
initialize() {}
@@ -16,7 +17,7 @@ export class HUDGameMenu extends BaseHUDPart {
keybinding: "menu_open_shop",
badge: () => this.root.hubGoals.getAvailableUpgradeCount(),
notification: /** @type {[string, enumNotificationType]} */ ([
"A new upgrade is available!",
T.ingame.notifications.newUpgrade,
enumNotificationType.upgrade,
]),
},

View File

@@ -3,6 +3,7 @@ import { makeDiv } from "../../../core/utils";
import { getStringForKeyCode } from "../../key_action_mapper";
import { TrackedState } from "../../../core/tracked_state";
import { queryParamOptions } from "../../../core/query_parameters";
import { T } from "../../../translations";
export class HUDKeybindingOverlay extends BaseHUDPart {
initialize() {
@@ -32,7 +33,7 @@ export class HUDKeybindingOverlay extends BaseHUDPart {
`
<div class="binding">
<code class="keybinding">${getKeycode("center_map")}</code>
<label>Center</label>
<label>${T.ingame.keybindingsOverlay.centerMap}</label>
</div>
<div class="binding">
@@ -41,48 +42,48 @@ export class HUDKeybindingOverlay extends BaseHUDPart {
<code class="keybinding">${getKeycode("map_move_left")}</code>
<code class="keybinding">${getKeycode("map_move_down")}</code>
<code class="keybinding">${getKeycode("map_move_right")}</code>
<label>Move</label>
<label>${T.ingame.keybindingsOverlay.moveMap}</label>
</div>
<div class="binding noPlacementOnly">
<code class="keybinding rightMouse"></code><i></i>
<code class="keybinding shift">CTRL</code>+
<code class="keybinding builtinKey">${T.global.keys.control}</code>+
<code class="keybinding leftMouse"></code>
<label>Delete</label>
<label>${T.ingame.keybindingsOverlay.removeBuildings}</label>
</div>
<div class="binding placementOnly">
<code class="keybinding rightMouse"></code> <i></i>
<code class="keybinding rightMouse"></code><i></i>
<code class="keybinding">${getKeycode("building_abort_placement")}</code>
<label>Stop placement</label>
<label>${T.ingame.keybindingsOverlay.stopPlacement}</label>
</div>
<div class="binding placementOnly">
<code class="keybinding">${getKeycode("rotate_while_placing")}</code>
<label>Rotate Building</label>
<label>${T.ingame.keybindingsOverlay.rotateBuilding}</label>
</div>
<div class="binding placementOnly">
<code class="keybinding shift">⇧ SHIFT</code>
<label>Place Multiple</label>
<code class="keybinding builtinKey shift">⇧ ${T.global.keys.shift}</code>
<label>${T.ingame.keybindingsOverlay.placeMultiple}</label>
</div>
<div class="binding placementOnly">
<code class="keybinding shift">ALT</code>
<label>Reverse orientation</label>
<code class="keybinding builtinKey">${T.global.keys.alt}</code>
<label>${T.ingame.keybindingsOverlay.reverseOrientation}</label>
</div>
<div class="binding placementOnly">
<code class="keybinding shift">CTRL</code>
<label>Disable auto orientation</label>
<code class="keybinding builtinKey">${T.global.keys.control}</code>
<label>${T.ingame.keybindingsOverlay.disableAutoOrientation}</label>
</div>
` +
(queryParamOptions.betaMode
? `
<div class="binding hudToggle">
<code class="keybinding">F2</code>
<label>Toggle HUD</label>
<label>${T.ingame.keybindingsOverlay.toggleHud}</label>
</div>
`
: "")

View File

@@ -9,6 +9,7 @@ import { makeDiv } from "../../../core/utils";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { createLogger } from "../../../core/logging";
import { enumMouseButton } from "../../camera";
import { T } from "../../../translations";
const logger = createLogger("hud/mass_selector");
@@ -23,10 +24,9 @@ export class HUDMassSelector extends BaseHUDPart {
parent,
"ingame_HUD_MassSelector",
[],
`
Press <code class="keybinding">${removalKeybinding}</code> to remove selected buildings
and <code class="keybinding">${abortKeybinding}</code> to cancel.
`
T.ingame.massDelete.infoText
.replace("<keyDelete>", removalKeybinding)
.replace("<keyCancel>", abortKeybinding)
);
}

View File

@@ -80,22 +80,6 @@ export class HUDModalDialogs extends BaseHUDPart {
return dialog.buttonSignals;
}
showVideoTutorial(title, text, videoUrl) {
const dialog = new DialogVideoTutorial({
app: this.app,
title: title,
contentHTML: text,
videoUrl,
});
this.internalShowDialog(dialog);
if (this.app) {
this.app.sound.playUiSound(SOUNDS.dialogOk);
}
return dialog.buttonSignals;
}
showOptionChooser(title, options) {
const dialog = new DialogOptionChooser({
app: this.app,

View File

@@ -1,5 +1,6 @@
import { BaseHUDPart } from "../base_hud_part";
import { makeDiv } from "../../../core/utils";
import { T } from "../../../translations";
/** @enum {string} */
export const enumNotificationType = {
@@ -23,7 +24,7 @@ export class HUDNotifications extends BaseHUDPart {
// Automatic notifications
this.root.signals.gameSaved.add(() =>
this.onNotification("Your game has been saved.", enumNotificationType.saved)
this.onNotification(T.ingame.notifications.gameSaved, enumNotificationType.saved)
);
}

View File

@@ -1,8 +1,9 @@
import { BaseHUDPart } from "../base_hud_part";
import { makeDiv } from "../../../core/utils";
import { makeDiv, formatSeconds } from "../../../core/utils";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { InputReceiver } from "../../../core/input_receiver";
import { KeyActionMapper } from "../../key_action_mapper";
import { T } from "../../../translations";
export class HUDSettingsMenu extends BaseHUDPart {
createElements(parent) {
@@ -14,18 +15,18 @@ export class HUDSettingsMenu extends BaseHUDPart {
this.background,
null,
["timePlayed"],
`<strong>Playtime</strong><span class="playtime"></span>`
`<strong>${T.ingame.settingsMenu.playtime}</strong><span class="playtime"></span>`
);
this.buttonContainer = makeDiv(this.menuElement, null, ["buttons"]);
const buttons = [
{
title: "Continue",
title: T.ingame.settingsMenu.buttons.continue,
action: () => this.close(),
},
{
title: "Return to menu",
title: T.ingame.settingsMenu.buttons.menu,
action: () => this.returnToMenu(),
},
];
@@ -79,9 +80,8 @@ export class HUDSettingsMenu extends BaseHUDPart {
// this.background.classList.add("visible");
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
const totalMinutesPlayed = Math.ceil(this.root.time.now() / 60.0);
const playtimeString = totalMinutesPlayed === 1 ? "1 minute" : totalMinutesPlayed + " minutes";
this.timePlayed.querySelector(".playtime").innerText = playtimeString;
const totalSecondsPlayed = Math.ceil(this.root.time.now());
this.timePlayed.querySelector(".playtime").innerText = formatSeconds(totalSecondsPlayed);
}
close() {

View File

@@ -1,12 +1,12 @@
import { BaseHUDPart } from "../base_hud_part";
import { makeDiv, removeAllChildren, formatBigNumber } from "../../../core/utils";
import { UPGRADES, TIER_LABELS } from "../../upgrades";
import { ShapeDefinition } from "../../shape_definition";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { InputReceiver } from "../../../core/input_receiver";
import { KeyActionMapper } from "../../key_action_mapper";
import { Math_min } from "../../../core/builtins";
import { ClickDetector } from "../../../core/click_detector";
import { InputReceiver } from "../../../core/input_receiver";
import { formatBigNumber, makeDiv } from "../../../core/utils";
import { T } from "../../../translations";
import { KeyActionMapper } from "../../key_action_mapper";
import { UPGRADES } from "../../upgrades";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
export class HUDShop extends BaseHUDPart {
createElements(parent) {
@@ -14,7 +14,7 @@ export class HUDShop extends BaseHUDPart {
// DIALOG Inner / Wrapper
this.dialogInner = makeDiv(this.background, null, ["dialogInner"]);
this.title = makeDiv(this.dialogInner, null, ["title"], `Upgrades`);
this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.shop.title);
this.closeButton = makeDiv(this.title, null, ["closeButton"]);
this.trackClicks(this.closeButton, this.close);
this.contentDiv = makeDiv(this.dialogInner, null, ["content"]);
@@ -23,7 +23,6 @@ export class HUDShop extends BaseHUDPart {
// Upgrades
for (const upgradeId in UPGRADES) {
const { label } = UPGRADES[upgradeId];
const handle = {};
handle.requireIndexToElement = [];
@@ -32,10 +31,10 @@ export class HUDShop extends BaseHUDPart {
handle.elem.setAttribute("data-upgrade-id", upgradeId);
// Title
const title = makeDiv(handle.elem, null, ["title"], label);
const title = makeDiv(handle.elem, null, ["title"], T.shopUpgrades[upgradeId].name);
// Title > Tier
handle.elemTierLabel = makeDiv(title, null, ["tier"], "Tier ?");
handle.elemTierLabel = makeDiv(title, null, ["tier"]);
// Icon
handle.icon = makeDiv(handle.elem, null, ["icon"]);
@@ -48,7 +47,7 @@ export class HUDShop extends BaseHUDPart {
// Buy button
handle.buyButton = document.createElement("button");
handle.buyButton.classList.add("buy", "styledButton");
handle.buyButton.innerText = "Upgrade";
handle.buyButton.innerText = T.ingame.shop.buttonUnlock;
handle.elem.appendChild(handle.buyButton);
this.trackClicks(handle.buyButton, () => this.tryUnlockNextTier(upgradeId));
@@ -61,13 +60,17 @@ export class HUDShop extends BaseHUDPart {
rerenderFull() {
for (const upgradeId in this.upgradeToElements) {
const handle = this.upgradeToElements[upgradeId];
const { description, tiers } = UPGRADES[upgradeId];
const { tiers } = UPGRADES[upgradeId];
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
const tierHandle = tiers[currentTier];
// Set tier
handle.elemTierLabel.innerText = "Tier " + TIER_LABELS[currentTier];
handle.elemTierLabel.innerText = T.ingame.shop.tier.replace(
"<x>",
"" + T.ingame.shop.tierLabels[currentTier]
);
handle.elemTierLabel.setAttribute("data-tier", currentTier);
// Cleanup detectors
@@ -84,12 +87,15 @@ export class HUDShop extends BaseHUDPart {
if (!tierHandle) {
// Max level
handle.elemDescription.innerText = "Maximum level";
handle.elemDescription.innerText = T.ingame.shop.maximumLevel;
continue;
}
// Set description
handle.elemDescription.innerText = description(tierHandle.improvement);
handle.elemDescription.innerText = T.shopUpgrades[upgradeId].description.replace(
"<gain>",
Math.floor(tierHandle.improvement * 100.0)
);
tierHandle.required.forEach(({ shape, amount }) => {
const container = makeDiv(handle.elemRequirements, null, ["requirement"]);

View File

@@ -1,18 +1,12 @@
import { Math_min } from "../../../core/builtins";
import { InputReceiver } from "../../../core/input_receiver";
import { makeButton, makeDiv, removeAllChildren } from "../../../core/utils";
import { makeButton, makeDiv, removeAllChildren, capitalizeFirstLetter } from "../../../core/utils";
import { KeyActionMapper } from "../../key_action_mapper";
import { enumAnalyticsDataSource } from "../../production_analytics";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { enumDisplayMode, HUDShapeStatisticsHandle } from "./statistics_handle";
const enumDataSourceToText = {
[enumAnalyticsDataSource.stored]: "Displaying amount of stored shapes in your central building.",
[enumAnalyticsDataSource.produced]:
"Displaying all shapes your whole factory produces, including intermediate products.",
[enumAnalyticsDataSource.delivered]: "Displaying shapes which are delivered to your central building.",
};
import { T } from "../../../translations";
export class HUDStatistics extends BaseHUDPart {
createElements(parent) {
@@ -20,7 +14,7 @@ export class HUDStatistics extends BaseHUDPart {
// DIALOG Inner / Wrapper
this.dialogInner = makeDiv(this.background, null, ["dialogInner"]);
this.title = makeDiv(this.dialogInner, null, ["title"], `statistics`);
this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.statistics.title);
this.closeButton = makeDiv(this.title, null, ["closeButton"]);
this.trackClicks(this.closeButton, this.close);
@@ -30,13 +24,21 @@ export class HUDStatistics extends BaseHUDPart {
this.filtersDataSource = makeDiv(this.filterHeader, null, ["filtersDataSource"]);
this.filtersDisplayMode = makeDiv(this.filterHeader, null, ["filtersDisplayMode"]);
const buttonModeProduced = makeButton(this.filtersDataSource, ["modeProduced"], "Produced");
const buttonModeDelivered = makeButton(this.filtersDataSource, ["modeDelivered"], "Delivered");
const buttonModeStored = makeButton(this.filtersDataSource, ["modeStored"], "Stored");
const dataSources = [
enumAnalyticsDataSource.produced,
enumAnalyticsDataSource.delivered,
enumAnalyticsDataSource.stored,
];
this.trackClicks(buttonModeProduced, () => this.setDataSource(enumAnalyticsDataSource.produced));
this.trackClicks(buttonModeStored, () => this.setDataSource(enumAnalyticsDataSource.stored));
this.trackClicks(buttonModeDelivered, () => this.setDataSource(enumAnalyticsDataSource.delivered));
for (let i = 0; i < dataSources.length; ++i) {
const dataSource = dataSources[i];
const button = makeButton(
this.filtersDataSource,
["mode" + capitalizeFirstLetter(dataSource)],
T.ingame.statistics.dataSources[dataSource].title
);
this.trackClicks(button, () => this.setDataSource(dataSource));
}
const buttonDisplayDetailed = makeButton(this.filtersDisplayMode, ["displayDetailed"]);
const buttonDisplayIcons = makeButton(this.filtersDisplayMode, ["displayIcons"]);
@@ -54,7 +56,7 @@ export class HUDStatistics extends BaseHUDPart {
this.dataSource = source;
this.dialogInner.setAttribute("data-datasource", source);
this.sourceExplanation.innerText = enumDataSourceToText[source];
this.sourceExplanation.innerText = T.ingame.statistics.dataSources[source].title;
if (this.visible) {
this.rerenderFull();
}
@@ -204,7 +206,7 @@ export class HUDStatistics extends BaseHUDPart {
if (entries.length === 0) {
this.contentDiv.innerHTML = `
<strong class="noEntries">No shapes have been produced so far.</strong>`;
<strong class="noEntries">${T.ingame.statistics.noShapesProduced}</strong>`;
}
this.contentDiv.classList.toggle("hasEntries", entries.length > 0);

View File

@@ -4,6 +4,7 @@ import { enumAnalyticsDataSource } from "../../production_analytics";
import { formatBigNumber, clamp } from "../../../core/utils";
import { globalConfig } from "../../../core/config";
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
import { T } from "../../../translations";
/** @enum {string} */
export const enumDisplayMode = {
@@ -86,7 +87,10 @@ export class HUDShapeStatisticsHandle {
(this.root.productionAnalytics.getCurrentShapeRate(dataSource, this.definition) /
globalConfig.analyticsSliceDurationSeconds) *
60;
this.counter.innerText = formatBigNumber(rate) + " / m";
this.counter.innerText = T.ingame.statistics.shapesPerMinute.replace(
"<shapes>",
formatBigNumber(rate)
);
break;
}
}

View File

@@ -10,9 +10,10 @@ import { MetaSplitterBuilding } from "../../buildings/splitter";
import { MetaStackerBuilding } from "../../buildings/stacker";
import { MetaTrashBuilding } from "../../buildings/trash";
import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt";
import { enumHubGoalRewards, enumHubGoalRewardToString } from "../../tutorial_goals";
import { enumHubGoalRewards } from "../../tutorial_goals";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { T } from "../../../translations";
export class HUDUnlockNotification extends BaseHUDPart {
initialize() {
@@ -36,17 +37,10 @@ export class HUDUnlockNotification extends BaseHUDPart {
const dialog = makeDiv(this.element, null, ["dialog"]);
this.elemTitle = makeDiv(dialog, null, ["title"], ``);
this.elemSubTitle = makeDiv(dialog, null, ["subTitle"], `Completed`);
this.elemTitle = makeDiv(dialog, null, ["title"]);
this.elemSubTitle = makeDiv(dialog, null, ["subTitle"], T.ingame.levelCompleteNotification.completed);
this.elemContents = makeDiv(
dialog,
null,
["contents"],
`
Ready for the next one?
`
);
this.elemContents = makeDiv(dialog, null, ["contents"]);
this.btnClose = document.createElement("button");
this.btnClose.classList.add("close", "styledButton");
@@ -61,9 +55,17 @@ export class HUDUnlockNotification extends BaseHUDPart {
* @param {enumHubGoalRewards} reward
*/
showForLevel(level, reward) {
this.elemTitle.innerText = "Level " + ("" + level).padStart(2, "0");
this.elemTitle.innerText = T.ingame.levelCompleteNotification.levelTitle.replace(
"<level>",
("" + level).padStart(2, "0")
);
let html = `<span class='reward'>Unlocked ${enumHubGoalRewardToString[reward]}!</span>`;
const rewardText = T.storyRewards[reward];
let html =
"<span class='reward'>" +
T.ingame.levelCompleteNotification.unlockText.replace("<reward>", rewardText) +
"</span>";
const addBuildingExplanation = metaBuildingClass => {
const metaBuilding = gMetaBuildingRegistry.findByClass(metaBuildingClass);

View File

@@ -6,6 +6,7 @@ import { Application } from "../application";
import { Signal, STOP_PROPAGATION } from "../core/signal";
import { IS_MOBILE } from "../core/config";
import { T } from "../translations";
function key(str) {
return str.toUpperCase().charCodeAt(0);
@@ -65,23 +66,23 @@ export function getStringForKeyCode(code) {
case 8:
return "⌫";
case 9:
return "TAB";
return T.global.keys.tab;
case 13:
return "⏎";
case 16:
return "⇪";
case 17:
return "CTRL";
return T.global.keys.control;
case 18:
return "ALT";
return T.global.keys.alt;
case 19:
return "PAUSE";
case 20:
return "CAPS";
case 27:
return "ESC";
return T.global.keys.escape;
case 32:
return "SPACE";
return T.global.keys.space;
case 33:
return "PGUP";
case 34:

View File

@@ -31,20 +31,6 @@ export class MetaBuilding {
return new Vector(1, 1);
}
/**
* Should return the name of this building
*/
getName() {
return this.id;
}
/**
* Should return the description of this building
*/
getDescription() {
return "No Description";
}
/**
* Whether to stay in placement mode after having placed a building
*/

View File

@@ -3,8 +3,8 @@ import { HubComponent } from "../components/hub";
import { DrawParameters } from "../../core/draw_parameters";
import { Entity } from "../entity";
import { formatBigNumber } from "../../core/utils";
import { enumHubGoalRewardToString } from "../tutorial_goals";
import { Loader } from "../../core/loader";
import { T } from "../../translations";
export class HubSystem extends GameSystemWithFilter {
constructor(root) {
@@ -84,7 +84,7 @@ export class HubSystem extends GameSystemWithFilter {
context.font = "bold 11px GameFont";
context.fillStyle = "#fd0752";
context.textAlign = "center";
context.fillText(enumHubGoalRewardToString[goals.reward].toUpperCase(), pos.x, pos.y + 46);
context.fillText(T.storyRewards[goals.reward].toUpperCase(), pos.x, pos.y + 46);
// Level
context.font = "bold 11px GameFont";

View File

@@ -15,21 +15,6 @@ export const enumHubGoalRewards = {
no_reward: "no_reward",
};
/**
* @enum {string}
*/
export const enumHubGoalRewardToString = {
[enumHubGoalRewards.reward_cutter_and_trash]: "Cutting Shapes",
[enumHubGoalRewards.reward_rotater]: "Rotating",
[enumHubGoalRewards.reward_painter]: "Painting",
[enumHubGoalRewards.reward_mixer]: "Color Mixing",
[enumHubGoalRewards.reward_stacker]: "Combiner",
[enumHubGoalRewards.reward_splitter]: "Splitter/Merger",
[enumHubGoalRewards.reward_tunnel]: "Tunnel",
[enumHubGoalRewards.no_reward]: "Next level",
};
export const tutorialGoals = [
// Circle
{

View File

@@ -1,33 +1,8 @@
import { findNiceIntegerValue } from "../core/utils";
import { ShapeDefinition } from "./shape_definition";
export const TIER_LABELS = [
"I",
"II",
"III",
"IV",
"V",
"VI",
"VII",
"VIII",
"IX",
"X",
"XI",
"XII",
"XIII",
"XIV",
"XV",
"XVI",
"XVII",
"XVIII",
"XIX",
"XX",
];
export const UPGRADES = {
belt: {
label: "Belts, Distributer & Tunnels",
description: improvement => "Speed +" + Math.floor(improvement * 100.0) + "%",
tiers: [
{
required: [{ shape: "CuCuCuCu", amount: 80 }],
@@ -49,8 +24,6 @@ export const UPGRADES = {
},
miner: {
label: "Extraction",
description: improvement => "Speed +" + Math.floor(improvement * 100.0) + "%",
tiers: [
{
required: [{ shape: "RuRuRuRu", amount: 200 }],
@@ -72,8 +45,6 @@ export const UPGRADES = {
},
processors: {
label: "Shape Processing",
description: improvement => "Speed +" + Math.floor(improvement * 100.0) + "%",
tiers: [
{
required: [{ shape: "SuSuSuSu", amount: 200 }],
@@ -95,8 +66,6 @@ export const UPGRADES = {
},
painting: {
label: "Mixing & Painting",
description: improvement => "Speed +" + Math.floor(improvement * 100.0) + "%",
tiers: [
{
required: [{ shape: "WuWuWuWu", amount: 200 }],

View File

@@ -7,6 +7,7 @@ import { createLogger } from "../../core/logging";
import { ClickDetector } from "../../core/click_detector";
import { performanceNow } from "../../core/builtins";
import { clamp } from "../../core/utils";
import { T } from "../../translations";
const logger = createLogger("adprovider/adinplay");
@@ -111,7 +112,7 @@ export class AdinplayAdProvider extends AdProviderInterface {
AD_HEIGHT: h,
AD_FULLSCREEN: false,
AD_CENTERPLAYER: false,
LOADING_TEXT: "Loading",
LOADING_TEXT: T.global.loading,
PREROLL_ELEM: function () {
return videoElement;
},

View File

@@ -10,6 +10,7 @@ import {
} from "../core/utils";
import { ReadWriteProxy } from "../core/read_write_proxy";
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
import { T } from "../translations";
export class MainMenuState extends GameState {
constructor() {
@@ -18,14 +19,12 @@ export class MainMenuState extends GameState {
getInnerHTML() {
const bannerHtml = `
<h3>This is a Demo Version</h3>
<h3>${T.demoBanners.title}</h3>
<p>Get <strong>shapez.io on steam</strong> for:</p>
<p>${T.demoBanners.intro}</p>
<ul>
<li>No advertisements and demo banners.</li>
<li>Unlimited savegame slots.</li>
<li>Supporting the developer ❤️</li>
${T.demoBanners.advantages.map(advantage => `<li>${advantage}</li>`).join("")}
</ul>
<a href="https://steam.shapez.io" class="steamLink" target="_blank">Get shapez.io on steam!</a>
@@ -65,12 +64,12 @@ export class MainMenuState extends GameState {
isSupportedBrowser()
? ""
: `
<div class="browserWarning">This game is optimized for Google Chrome. Your browser is not supported or slow!</div>
<div class="browserWarning">${T.mainMenu.browserWarning}</div>
`
}
<button class="playButton styledButton">Play</button>
<button class="importButton styledButton">Import savegame</button>
<button class="playButton styledButton">${T.mainMenu.play}</button>
<button class="importButton styledButton">${T.mainMenu.importSavegame}</button>
</div>
${
@@ -86,13 +85,13 @@ export class MainMenuState extends GameState {
<div class="footer">
<a href="https://github.com/tobspr/shapez.io" target="_blank">
This game is open source!
${T.mainMenu.openSourceHint}
<span class="thirdpartyLogo githubLogo"></span>
</a>
</a>
<a href="https://discord.gg/HN7EVzV" target="_blank">
Official discord server
<span class="thirdpartyLogo discordLogo"></span>
${T.mainMenu.discordLink}
<span class="thirdpartyLogo discordLogo"></span>
</a>
</div>
@@ -112,7 +111,6 @@ export class MainMenuState extends GameState {
const reader = new FileReader();
reader.addEventListener("load", event => {
const contents = event.target.result;
let realContent;
try {
@@ -120,8 +118,8 @@ export class MainMenuState extends GameState {
} catch (err) {
closeLoader();
this.dialogs.showWarning(
"Import error",
"Failed to import your savegame:<br><br>" + err
T.dialogs.importSavegameError.title,
T.dialogs.importSavegameError.text + "<br><br>" + err
);
return;
}
@@ -129,22 +127,27 @@ export class MainMenuState extends GameState {
this.app.savegameMgr.importSavegame(realContent).then(
() => {
closeLoader();
this.dialogs.showWarning("Imported", "Your savegame has been imported.");
this.dialogs.showWarning(
T.dialogs.importSavegameSuccess.title,
T.dialogs.importSavegameSuccess.text
);
this.renderSavegames();
},
err => {
closeLoader();
this.dialogs.showWarning(
"Import error",
"Failed to import savegame. Please check the console output."
T.dialogs.importSavegameError.title,
T.dialogs.importSavegameError.text + ":<br><br>" + err
);
}
);
});
reader.addEventListener("error", error => {
console.error(error);
alert("Failed to read file: " + error);
this.dialogs.showWarning(
T.dialogs.importSavegameError.title,
T.dialogs.importSavegameError.text + ":<br><br>" + error
);
});
reader.readAsText(file, "utf-8");
});
@@ -159,7 +162,10 @@ export class MainMenuState extends GameState {
onEnter(payload) {
if (payload.loadError) {
alert("Error while loading game: " + payload.loadError);
this.dialogs.showWarning(
T.dialogs.gameLoadFailure.title,
T.dialogs.gameLoadFailure.text + "<br><br>" + payload.loadError
);
}
this.dialogs = new HUDModalDialogs(null, this.app);
@@ -244,8 +250,8 @@ export class MainMenuState extends GameState {
*/
deleteGame(game) {
const signals = this.dialogs.showWarning(
"Confirm Deletion",
"Are you sure you want to delete the game?",
T.dialogs.confirmSavegameDelete.title,
T.dialogs.confirmSavegameDelete.text,
["delete:bad", "cancel:good"]
);
@@ -255,7 +261,10 @@ export class MainMenuState extends GameState {
this.renderSavegames();
},
err => {
this.dialogs.showWarning("Failed to delete", "Error: " + err);
this.dialogs.showWarning(
T.dialogs.savegameDeletionError.title,
T.dialogs.savegameDeletionError.text + "<br><br>" + err
);
}
);
});

20
src/js/translations.js Normal file
View File

@@ -0,0 +1,20 @@
import { globalConfig } from "./core/config";
const baseTranslations = require("./translations-built/base-en.json");
export const T = baseTranslations;
if (G_IS_DEV && globalConfig.debug.testTranslations) {
// Replaces all translations by fake translations to see whats translated and what not
const mapTranslations = obj => {
for (const key in obj) {
const value = obj[key];
if (typeof value === "string") {
obj[key] = value.replace(/[a-z]/gi, "x");
} else {
mapTranslations(value);
}
}
};
mapTranslations(T);
}