diff --git a/config/config.exs b/config/config.exs
index 6b396e2..dedbed2 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -10,7 +10,19 @@ import Config
config :helldivers_2,
generators: [timestamp_type: :utc_datetime],
war_season: "801",
- war_seasons: ["801", "805"]
+ war_seasons: ["801", "805"],
+ # default language
+ language: :en,
+ # all available languages
+ languages: [
+ en: "en-US",
+ de: "de-DE",
+ es: "es-ES",
+ ru: "ru-RU",
+ fr: "fr-FR",
+ it: "it-IT",
+ pl: "pl-PL"
+ ]
# Configures the endpoint
config :helldivers_2, Helldivers2Web.Endpoint,
diff --git a/config/test.exs b/config/test.exs
index df7f28c..4bde470 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -2,7 +2,12 @@ import Config
# We don't start up any Helldiver seasons during tests to avoid hitting the API
config :helldivers_2,
- war_seasons: []
+ war_seasons: [],
+ # Limit languages available in testing
+ languages: [
+ en: "en-US",
+ de: "de-DE"
+ ]
# We don't run a server during test. If one is required,
# you can enable the server option below.
diff --git a/lib/helldivers_2/models/war_status.ex b/lib/helldivers_2/models/war_status.ex
index cf68b1a..d64b448 100644
--- a/lib/helldivers_2/models/war_status.ex
+++ b/lib/helldivers_2/models/war_status.ex
@@ -6,6 +6,7 @@ defmodule Helldivers2.Models.WarStatus do
community targets, joint operations, planet events,
and global events.
"""
+ require Logger
alias Helldivers2.WarSeason
alias Helldivers2.Models.WarInfo.Planet
alias Helldivers2.Models.WarStatus.PlanetEvent
@@ -46,27 +47,36 @@ defmodule Helldivers2.Models.WarStatus do
@spec download(String.t()) :: {:ok, t()} | {:error, term()}
def download(war_id) do
- with {:ok, response} <-
- Req.get("https://api.live.prod.thehelldiversgame.com/api/WarSeason/#{war_id}/Status"),
- %Req.Response{status: 200, body: payload} <- response do
- {:ok, parse(payload)}
- else
- %Req.Response{status: status} ->
- {:error, "API error #{status}"}
- end
+ default_language = Application.get_env(:helldivers_2, :language)
+
+ translations = :helldivers_2
+ |> Application.get_env(:languages)
+ |> Task.async_stream(fn {key, lang} -> {key, download_language!(war_id, lang)} end, timeout: :infinity, zip_input_on_exit: true)
+ |> Enum.reduce(%{}, fn ({:ok, {key, payload}}, acc) ->
+ Map.put(acc, key, payload)
+ end)
+
+ # Take the default language as 'base' response object.
+ base = Map.get(translations, default_language)
+
+ {:ok, parse(base, translations)}
end
@doc """
Attempts to parse as much information as possible from the given `map` into a struct.
"""
- @spec parse(map()) :: t()
- def parse(map) when is_map(map) do
+ @spec parse(map(), %{atom() => map()}) :: t()
+ def parse(map, translations \\ %{}) when is_map(map) do
war_id = Map.get(map, "warId")
campaigns = Enum.map(Map.get(map, "campaigns"), &Campaign.parse(war_id, &1))
joint_operations =
Enum.map(Map.get(map, "jointOperations"), &JointOperation.parse(war_id, &1))
+ # We currently only translate global events
+ global_event_translations = translations
+ |> Map.new(fn {key, payload} -> {key, Map.get(payload, "globalEvents")} end)
+
%__MODULE__{
war_id: war_id,
snapshot_at: DateTime.from_unix!(Map.get(map, "time")),
@@ -91,8 +101,27 @@ defmodule Helldivers2.Models.WarStatus do
active_election_policy_effects: [],
global_events: map
|> Map.get("globalEvents")
- |> Enum.map(&GlobalEvent.parse(war_id, &1))
+ |> Enum.map(&GlobalEvent.parse(war_id, &1, global_event_translations))
|> Enum.sort(fn (event1, event2) -> event1.id > event2.id end)
}
end
+
+ @spec download_language!(String.t(), String.t()) :: map() | no_return()
+ defp download_language!(war_id, language) do
+ Logger.debug("Fetching #{language} for #{war_id}")
+
+ response =
+ [url: "https://api.live.prod.thehelldiversgame.com/api/WarSeason/#{war_id}/Status", retry: :transient]
+ |> Req.new()
+ |> Req.Request.put_header("accept-language", language)
+ |> Req.request!()
+
+ case response do
+ %Req.Response{status: 200} ->
+ response.body
+
+ %Req.Response{status: status} ->
+ raise "API returned an error #{status}"
+ end
+ end
end
diff --git a/lib/helldivers_2/models/war_status/global_event.ex b/lib/helldivers_2/models/war_status/global_event.ex
index bd2efd9..f8d3086 100644
--- a/lib/helldivers_2/models/war_status/global_event.ex
+++ b/lib/helldivers_2/models/war_status/global_event.ex
@@ -10,7 +10,7 @@ defmodule Helldivers2.Models.WarStatus.GlobalEvent do
portrait_id_32: non_neg_integer(),
title: String.t(),
title_32: non_neg_integer(),
- message: String.t(),
+ message: %{String.t() => String.t()},
message_id_32: non_neg_integer(),
race: Faction.t(),
flag: non_neg_integer(),
@@ -36,16 +36,32 @@ defmodule Helldivers2.Models.WarStatus.GlobalEvent do
@doc """
Attempts to parse as much information as possible from the given `map` into a struct.
+
+ Takes an optional map of translations, where each key is the language
+ and each value is the full list of all global events available.
"""
@spec parse(String.t(), map()) :: t()
- def parse(war_id, map) when is_map(map) do
+ def parse(war_id, map, translations \\ %{}) when is_map(map) do
+ # Get the ID of the base entity so we can fetch all matching entities from translations.
+ id = Map.get(map, "eventId")
+
+ # Filter out our translations map to only include the currently being processed message.
+ translations = translations
+ |> Map.new(fn {lang, events} ->
+ event = events
+ |> Enum.find(%{}, fn event -> Map.get(event, "eventId") == id end)
+ |> Map.get("message")
+
+ {lang, event}
+ end)
+
%__MODULE__{
- id: Map.get(map, "eventId"),
+ id: id,
id_32: Map.get(map, "id32"),
portrait_id_32: Map.get(map, "portraitId32"),
title: Map.get(map, "title"),
title_32: Map.get(map, "titleId32"),
- message: Map.get(map, "message"),
+ message: translations,
message_id_32: Map.get(map, "messageId32"),
race: Faction.parse(Map.get(map, "race")),
flag: Map.get(map, "flag"),
diff --git a/lib/helldivers_2_web/schemas/global_event_schema.ex b/lib/helldivers_2_web/schemas/global_event_schema.ex
index a854062..e9475af 100644
--- a/lib/helldivers_2_web/schemas/global_event_schema.ex
+++ b/lib/helldivers_2_web/schemas/global_event_schema.ex
@@ -3,6 +3,8 @@ defmodule Helldivers2Web.Schemas.GlobalEventSchema do
alias OpenApiSpex.Schema
require OpenApiSpex
+ @languages Application.compile_env(:helldivers_2, :languages)
+
@doc "Generates a schema for a single homeworld schema response"
def response(),
do:
@@ -29,7 +31,8 @@ defmodule Helldivers2Web.Schemas.GlobalEventSchema do
},
title: %Schema{
type: :string,
- description: "The title text, for some reason this may not always be English"
+ description: "The title of the global event, appears to be more a status than an actual title",
+ enum: ["BRIEFING", "SUCCESS", "FAILED"]
},
title_32: %Schema{
type: :integer,
@@ -37,8 +40,15 @@ defmodule Helldivers2Web.Schemas.GlobalEventSchema do
"Internal identifier of the title, this always remains the same regardless of language"
},
message: %Schema{
- type: :string,
- description: "The message from Super Earth about the global event"
+ type: :object,
+ properties:
+ Map.new(@languages, fn {key, lang} ->
+ {key,
+ %Schema{
+ type: :string,
+ description: "The message from Super Earth about the global event in #{lang}"
+ }}
+ end)
},
message_id_32: %Schema{
type: :integer,
diff --git a/test/support/fixtures/war_season_fixtures.ex b/test/support/fixtures/war_season_fixtures.ex
index 1a3346b..884596d 100644
--- a/test/support/fixtures/war_season_fixtures.ex
+++ b/test/support/fixtures/war_season_fixtures.ex
@@ -74,7 +74,7 @@ defmodule Helldivers2.WarSeasonFixtures do
health: 1_000_000,
regen_per_second: 138.88889,
players: 1728,
- liberation: 100,
+ liberation: 100
},
%PlanetStatus{
planet: planet_klen_dath_fixture(),
@@ -82,7 +82,7 @@ defmodule Helldivers2.WarSeasonFixtures do
health: 1_000_000,
regen_per_second: 138.88889,
players: 0,
- liberation: 100,
+ liberation: 100
}
],
planet_attacks: [
@@ -144,8 +144,12 @@ defmodule Helldivers2.WarSeasonFixtures do
portrait_id_32: 0,
title: "SUCCESS",
title_32: 2_998_873_950,
- message:
- "The path to the Barrier Planets is clear. \n\nUpon each Barrier Planet, SEAF Engineers have begun construction of the Terminid Control System (TCS): a network of massive towers that will cover each planet in the well-tested neurotoxin known as Termicide.\n\nSoon, our citizens will be able to rest easy, knowing their children are safe from the threat of being eaten alive.",
+ message: %{
+ "en" =>
+ "Well done, Helldivers. SEAF Containment Teams will take over cleanup and inoculation operations from here. Colonists have been advised that there is absolutely zero threat of further Terminid violence, and to settle their families with total peace of mind.\n\nAdditionally, the pilots allocated to spore clearance on Veld have now been redirected to Helldiver support operations. \nExtraction will be faster on all operations. ",
+ "de" =>
+ "Gut gemacht, Helldivers. S.E.A.F.-Sicherungsteams übernehmen ab hier die Säuberungs- und Impfaufgaben. Die Kolonisten wurden darüber informiert, dass absolut keine Gefahr weiterer Bedrohungen durch Terminiden besteht und dass sie sich in aller Ruhe mit ihren Familien niederlassen können.\n\nDarüber hinaus wurden die Piloten, die die Sporen auf Veld beseitigen sollten, nun angewiesen, die Helldivers bei ihren Bemühungen zu unterstützen.\nDie Evakuierung geht bei allen Operationen schneller vonstatten."
+ },
message_id_32: 2_450_763_514,
race: "Humans",
flag: 2,
@@ -159,8 +163,12 @@ defmodule Helldivers2.WarSeasonFixtures do
portrait_id_32: 0,
title: "BRIEFING",
title_32: 2_908_633_975,
- message:
- "The Automatons have launched cowardly surprise attacks on multiple highly-populated Super Earth planets. The Super Earth Armed Forces in the region have been overrun. Millions of citizens are in grave danger of death, or disenfranchisement.\n\nThis grievous attack on Freedom will not go unanswered. The homes of our citizens must be defended.",
+ message: %{
+ "en" =>
+ "Despite the valorous efforts of the Helldivers, Automaton marauders have invaded Super Earth territory. Patriotic citizens mourn as their sufficiently-sized homes burn to the ground.\n\nSuper Earth citizens demand justice, and they will receive it.",
+ "de" =>
+ "Trotz der mutigen Anstrengungen der Helldivers konnten Roboter-Marodeure in Über-Erde-Gebiete eindringen. Patriotische Bürger verfallen in tiefe Trauer, während ihre ausreichend großen Behausungen niederbrennen.\n\nDie Bürger von Über-Erde verlangen Gerechtigkeit und sie werden sie bekommen."
+ },
message_id_32: 2_667_498_758,
race: "Humans",
flag: 1,