feat: add more localisation support
This commit is contained in:
parent
146abcfc9a
commit
1d67debfd0
|
@ -2,6 +2,6 @@ author: ascclemens
|
|||
name: Remote Party Finder Uploader
|
||||
description: |-
|
||||
Uploads the PF listings you retrieve to the crowdsourced Remote
|
||||
Party Finder website (https://rpf.annaclemens.io/).
|
||||
punchline: Crowdsourced PF website
|
||||
Party Finder website (https://xivpf.com/).
|
||||
punchline: Crowdsourced PF website (upload PF listings to xivpf.com)
|
||||
repo_url: https://git.sr.ht/~jkcclemens/remote-party-finder
|
||||
|
|
|
@ -11,7 +11,13 @@ using Lumina.Text;
|
|||
namespace SourceGenerator {
|
||||
internal class Program {
|
||||
private static void Main(string[] args) {
|
||||
var data = new GameData(args[0]);
|
||||
var data = new Dictionary<Language, GameData>(4);
|
||||
foreach (var lang in Languages.Keys) {
|
||||
data[lang] = new GameData(args[0], new LuminaOptions {
|
||||
DefaultExcelLanguage = lang,
|
||||
});
|
||||
}
|
||||
|
||||
var prog = new Program(data);
|
||||
|
||||
File.WriteAllText(Path.Join(args[1], "duties.rs"), prog.GenerateDuties());
|
||||
|
@ -23,9 +29,9 @@ namespace SourceGenerator {
|
|||
File.WriteAllText(Path.Join(args[1], "treasure_maps.rs"), prog.GenerateTreasureMaps());
|
||||
}
|
||||
|
||||
private GameData Data { get; }
|
||||
private Dictionary<Language, GameData> Data { get; }
|
||||
|
||||
private Program(GameData data) {
|
||||
private Program(Dictionary<Language, GameData> data) {
|
||||
this.Data = data;
|
||||
}
|
||||
|
||||
|
@ -46,8 +52,8 @@ namespace SourceGenerator {
|
|||
[Language.French] = "fr",
|
||||
};
|
||||
|
||||
private string? GetLocalisedStruct<T>(uint rowId, Func<T, SeString?> nameFunc, uint indent = 0) where T : ExcelRow {
|
||||
var def = this.Data.GetExcelSheet<T>()!.GetRow(rowId)!;
|
||||
private string? GetLocalisedStruct<T>(uint rowId, Func<T, SeString?> nameFunc, uint indent = 0, bool capitalise = false) where T : ExcelRow {
|
||||
var def = this.Data[Language.English].GetExcelSheet<T>()!.GetRow(rowId)!;
|
||||
var defName = nameFunc(def)?.TextValue();
|
||||
if (string.IsNullOrEmpty(defName)) {
|
||||
return null;
|
||||
|
@ -58,11 +64,14 @@ namespace SourceGenerator {
|
|||
sb.Append("LocalisedText {\n");
|
||||
|
||||
foreach (var (language, key) in Languages) {
|
||||
var row = this.Data.GetExcelSheet<T>(language)?.GetRow(rowId);
|
||||
var row = this.Data[language].GetExcelSheet<T>(language)?.GetRow(rowId);
|
||||
var name = row == null
|
||||
? defName
|
||||
: nameFunc(row)?.TextValue().Replace("\"", "\\\"");
|
||||
name ??= defName;
|
||||
if (capitalise) {
|
||||
name = name[..1].ToUpperInvariant() + name[1..];
|
||||
}
|
||||
|
||||
for (var i = 0; i < indent + 4; i++) {
|
||||
sb.Append(' ');
|
||||
|
@ -74,7 +83,7 @@ namespace SourceGenerator {
|
|||
for (var i = 0; i < indent; i++) {
|
||||
sb.Append(' ');
|
||||
}
|
||||
|
||||
|
||||
sb.Append('}');
|
||||
|
||||
return sb.ToString();
|
||||
|
@ -95,7 +104,7 @@ namespace SourceGenerator {
|
|||
sb.Append("#[allow(unused)]\n");
|
||||
sb.Append("#[repr(u32)]\n");
|
||||
sb.Append("pub enum ContentKind {\n");
|
||||
foreach (var kind in this.Data.GetExcelSheet<ContentType>()!) {
|
||||
foreach (var kind in this.Data[Language.English].GetExcelSheet<ContentType>()!) {
|
||||
var name = kind.Name.TextValue().Replace(" ", "");
|
||||
if (name.Length > 0) {
|
||||
sb.Append($" {name} = {kind.RowId},\n");
|
||||
|
@ -109,7 +118,7 @@ namespace SourceGenerator {
|
|||
|
||||
sb.Append(" fn from_u32(kind: u32) -> Self {\n");
|
||||
sb.Append(" match kind {\n");
|
||||
foreach (var kind in this.Data.GetExcelSheet<ContentType>()!) {
|
||||
foreach (var kind in this.Data[Language.English].GetExcelSheet<ContentType>()!) {
|
||||
var name = kind.Name.TextValue().Replace(" ", "");
|
||||
if (name.Length > 0) {
|
||||
sb.Append($" {kind.RowId} => Self::{name},\n");
|
||||
|
@ -122,7 +131,7 @@ namespace SourceGenerator {
|
|||
|
||||
sb.Append(" pub fn as_u32(self) -> u32 {\n");
|
||||
sb.Append(" match self {\n");
|
||||
foreach (var kind in this.Data.GetExcelSheet<ContentType>()!) {
|
||||
foreach (var kind in this.Data[Language.English].GetExcelSheet<ContentType>()!) {
|
||||
var name = kind.Name.TextValue().Replace(" ", "");
|
||||
if (name.Length > 0) {
|
||||
sb.Append($" Self::{name} => {kind.RowId},\n");
|
||||
|
@ -138,20 +147,19 @@ namespace SourceGenerator {
|
|||
sb.Append("lazy_static::lazy_static! {\n");
|
||||
sb.Append(" pub static ref DUTIES: HashMap<u32, DutyInfo> = maplit::hashmap! {\n");
|
||||
|
||||
foreach (var cfc in this.Data.GetExcelSheet<ContentFinderCondition>()!) {
|
||||
foreach (var cfc in this.Data[Language.English].GetExcelSheet<ContentFinderCondition>()!) {
|
||||
if (cfc.RowId == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var name = this.GetLocalisedStruct<ContentFinderCondition>(cfc.RowId, row => row.Name, 12);
|
||||
var name = this.GetLocalisedStruct<ContentFinderCondition>(cfc.RowId, row => row.Name, 12, true);
|
||||
if (name == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
name = name[..1].ToUpperInvariant() + name[1..];
|
||||
var highEnd = cfc.HighEndDuty ? "true" : "false";
|
||||
var contentType = cfc.ContentType.Value;
|
||||
var contentKind = contentType?.Name?.TextValue()?.Replace(" ", "");
|
||||
var contentKind = contentType?.Name?.TextValue().Replace(" ", "");
|
||||
if (string.IsNullOrEmpty(contentKind)) {
|
||||
contentKind = $"Other({contentType?.RowId ?? 0})";
|
||||
}
|
||||
|
@ -175,7 +183,7 @@ namespace SourceGenerator {
|
|||
sb.Append("lazy_static::lazy_static! {\n");
|
||||
sb.Append(" pub static ref JOBS: HashMap<u32, ClassJob> = maplit::hashmap! {\n");
|
||||
|
||||
foreach (var cj in this.Data.GetExcelSheet<ClassJob>()!) {
|
||||
foreach (var cj in this.Data[Language.English].GetExcelSheet<ClassJob>()!) {
|
||||
if (cj.RowId == 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -218,7 +226,7 @@ namespace SourceGenerator {
|
|||
sb.Append("lazy_static::lazy_static! {\n");
|
||||
sb.Append(" pub static ref ROULETTES: HashMap<u32, RouletteInfo> = maplit::hashmap! {\n");
|
||||
|
||||
foreach (var cr in this.Data.GetExcelSheet<ContentRoulette>()!) {
|
||||
foreach (var cr in this.Data[Language.English].GetExcelSheet<ContentRoulette>()!) {
|
||||
if (cr.RowId == 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -250,7 +258,7 @@ namespace SourceGenerator {
|
|||
sb.Append("lazy_static::lazy_static! {\n");
|
||||
sb.Append(" pub static ref WORLDS: HashMap<u32, World> = maplit::hashmap! {\n");
|
||||
|
||||
foreach (var world in this.Data.GetExcelSheet<World>()!) {
|
||||
foreach (var world in this.Data[Language.English].GetExcelSheet<World>()!) {
|
||||
if (world.RowId == 0 || !world.IsPublic || world.DataCenter.Row == 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -274,7 +282,7 @@ namespace SourceGenerator {
|
|||
sb.Append("\nlazy_static::lazy_static! {\n");
|
||||
sb.Append(" pub static ref TERRITORY_NAMES: HashMap<u32, LocalisedText> = maplit::hashmap! {\n");
|
||||
|
||||
foreach (var tt in this.Data.GetExcelSheet<TerritoryType>()!) {
|
||||
foreach (var tt in this.Data[Language.English].GetExcelSheet<TerritoryType>()!) {
|
||||
if (tt.RowId == 0 || tt.PlaceName.Row == 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -302,7 +310,7 @@ namespace SourceGenerator {
|
|||
sb.Append("\nlazy_static::lazy_static! {\n");
|
||||
sb.Append(" pub static ref AUTO_TRANSLATE: HashMap<(u32, u32), LocalisedText> = maplit::hashmap! {\n");
|
||||
|
||||
foreach (var row in this.Data.GetExcelSheet<Completion>()!) {
|
||||
foreach (var row in this.Data[Language.English].GetExcelSheet<Completion>()!) {
|
||||
var lookup = row.LookupTable.TextValue();
|
||||
if (lookup is not ("" or "@")) {
|
||||
// TODO: do lookup
|
||||
|
@ -326,19 +334,26 @@ namespace SourceGenerator {
|
|||
sb.Append(" pub static ref TREASURE_MAPS: HashMap<u32, LocalisedText> = maplit::hashmap! {\n");
|
||||
sb.Append(" 0 => LocalisedText {\n");
|
||||
sb.Append(" en: \"All Levels\",\n");
|
||||
sb.Append(" ja: \"All Levels\",\n");
|
||||
sb.Append(" de: \"All Levels\",\n");
|
||||
sb.Append(" fr: \"All Levels\",\n");
|
||||
sb.Append(" ja: \"レベルを指定しない\",\n");
|
||||
sb.Append(" de: \"Jede Stufe\",\n");
|
||||
sb.Append(" fr: \"Tous niveaux\",\n");
|
||||
sb.Append(" },\n");
|
||||
|
||||
var i = 1;
|
||||
foreach (var row in this.Data.GetExcelSheet<TreasureHuntRank>()!) {
|
||||
foreach (var row in this.Data[Language.English].GetExcelSheet<TreasureHuntRank>()!) {
|
||||
// IS THIS RIGHT?
|
||||
if (row.TreasureHuntTexture != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var name = this.GetLocalisedStruct<TreasureHuntRank>(row.RowId, row => row.KeyItemName.Value?.Name, 8);
|
||||
SeString? GetMapName(TreasureHuntRank thr) {
|
||||
var name = thr.KeyItemName.Value?.Name;
|
||||
return string.IsNullOrEmpty(name?.TextValue())
|
||||
? thr.ItemName.Value?.Name
|
||||
: name;
|
||||
}
|
||||
|
||||
var name = this.GetLocalisedStruct<TreasureHuntRank>(row.RowId, GetMapName, 8);
|
||||
if (!string.IsNullOrEmpty(name)) {
|
||||
sb.Append($" {i++} => {name},\n");
|
||||
}
|
||||
|
|
|
@ -84,6 +84,11 @@ details > summary {
|
|||
margin-top: 1em;
|
||||
}
|
||||
|
||||
#container > .settings {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#listings > .no-listings {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
|
||||
const state = {
|
||||
allowed: [],
|
||||
centre: "All",
|
||||
centre: 'All',
|
||||
list: null,
|
||||
lang: 'en',
|
||||
};
|
||||
|
||||
function addJsClass() {
|
||||
|
@ -54,12 +55,14 @@
|
|||
state.allowed.push(option.value);
|
||||
}
|
||||
|
||||
console.log(`allowed includes ${option.value} = ${state.allowed.includes(option.value)}`)
|
||||
option.selected = state.allowed.includes(option.value);
|
||||
}
|
||||
|
||||
let dataCentre = document.getElementById('data-centre-filter');
|
||||
dataCentre.value = state.centre;
|
||||
|
||||
let language = document.getElementById('language');
|
||||
language.value = state.lang;
|
||||
}
|
||||
|
||||
function setUpList() {
|
||||
|
@ -143,9 +146,19 @@
|
|||
});
|
||||
}
|
||||
|
||||
function setUpLanguage() {
|
||||
let language = document.getElementById('language');
|
||||
language.addEventListener('change', () => {
|
||||
state.lang = language.value;
|
||||
document.cookie = `lang=${encodeURIComponent(language.value)};path=/;max-age=31536000;samesite=lax`;
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
addJsClass();
|
||||
saveLoadState();
|
||||
reflectState();
|
||||
setUpLanguage();
|
||||
state.list = setUpList();
|
||||
setUpDataCentreFilter();
|
||||
setUpCategoryFilter();
|
||||
|
|
|
@ -21,16 +21,21 @@ use std::{
|
|||
str::FromStr,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LocalisedText {
|
||||
pub en: &'static str,
|
||||
pub ja: &'static str,
|
||||
pub de: &'static str,
|
||||
pub fr: &'static str,
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Language {
|
||||
English,
|
||||
Japanese,
|
||||
German,
|
||||
French,
|
||||
}
|
||||
|
||||
impl LocalisedText {
|
||||
pub fn from_codes(&self, val: &str) -> &'static str {
|
||||
impl Language {
|
||||
pub fn from_codes(val: Option<&str>) -> Self {
|
||||
let val = match val {
|
||||
Some(v) => v,
|
||||
None => return Self::English,
|
||||
};
|
||||
|
||||
let mut parts: Vec<(&str, f32)> = val.split(',')
|
||||
.map(|part| {
|
||||
let sub_parts: Vec<&str> = part.split(';').collect();
|
||||
|
@ -45,20 +50,39 @@ impl LocalisedText {
|
|||
.collect();
|
||||
parts.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Less));
|
||||
if parts.len() == 0 {
|
||||
return self.en;
|
||||
return Self::English;
|
||||
}
|
||||
|
||||
for (lang, _) in parts {
|
||||
let first = lang.split('-').next().unwrap();
|
||||
match first {
|
||||
"en" => return self.en,
|
||||
"ja" => return self.ja,
|
||||
"de" => return self.de,
|
||||
"fr" => return self.fr,
|
||||
"en" => return Self::English,
|
||||
"ja" => return Self::Japanese,
|
||||
"de" => return Self::German,
|
||||
"fr" => return Self::French,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
self.en
|
||||
Self::English
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LocalisedText {
|
||||
pub en: &'static str,
|
||||
pub ja: &'static str,
|
||||
pub de: &'static str,
|
||||
pub fr: &'static str,
|
||||
}
|
||||
|
||||
impl LocalisedText {
|
||||
pub fn text(&self, lang: &Language) -> &'static str {
|
||||
match lang {
|
||||
Language::English => self.en,
|
||||
Language::Japanese => self.ja,
|
||||
Language::German => self.de,
|
||||
Language::French => self.fr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -5,105 +5,105 @@ lazy_static::lazy_static! {
|
|||
pub static ref TREASURE_MAPS: HashMap<u32, LocalisedText> = maplit::hashmap! {
|
||||
0 => LocalisedText {
|
||||
en: "All Levels",
|
||||
ja: "All Levels",
|
||||
de: "All Levels",
|
||||
fr: "All Levels",
|
||||
ja: "レベルを指定しない",
|
||||
de: "Jede Stufe",
|
||||
fr: "Tous niveaux",
|
||||
},
|
||||
1 => LocalisedText {
|
||||
en: "Leather Treasure Map",
|
||||
ja: "Leather Treasure Map",
|
||||
de: "Leather Treasure Map",
|
||||
fr: "Leather Treasure Map",
|
||||
ja: "古ぼけた地図G1",
|
||||
de: "Leder-Schatzkarte",
|
||||
fr: "Carte au trésor en cuir",
|
||||
},
|
||||
2 => LocalisedText {
|
||||
en: "Leather Treasure Map",
|
||||
ja: "Leather Treasure Map",
|
||||
de: "Leather Treasure Map",
|
||||
fr: "Leather Treasure Map",
|
||||
ja: "古ぼけた地図G1",
|
||||
de: "Leder-Schatzkarte",
|
||||
fr: "Carte au trésor en cuir",
|
||||
},
|
||||
3 => LocalisedText {
|
||||
en: "Goatskin Treasure Map",
|
||||
ja: "Goatskin Treasure Map",
|
||||
de: "Goatskin Treasure Map",
|
||||
fr: "Goatskin Treasure Map",
|
||||
ja: "古ぼけた地図G2",
|
||||
de: "Steinbockleder-Schatzkarte",
|
||||
fr: "Carte au trésor en peau de bouquetin",
|
||||
},
|
||||
4 => LocalisedText {
|
||||
en: "Toadskin Treasure Map",
|
||||
ja: "Toadskin Treasure Map",
|
||||
de: "Toadskin Treasure Map",
|
||||
fr: "Toadskin Treasure Map",
|
||||
ja: "古ぼけた地図G3",
|
||||
de: "Krötenleder-Schatzkarte",
|
||||
fr: "Carte au trésor en peau de crapaud",
|
||||
},
|
||||
5 => LocalisedText {
|
||||
en: "Boarskin Treasure Map",
|
||||
ja: "Boarskin Treasure Map",
|
||||
de: "Boarskin Treasure Map",
|
||||
fr: "Boarskin Treasure Map",
|
||||
ja: "古ぼけた地図G4",
|
||||
de: "Keilerleder-Schatzkarte",
|
||||
fr: "Carte au trésor en peau de sanglier",
|
||||
},
|
||||
6 => LocalisedText {
|
||||
en: "Peisteskin Treasure Map",
|
||||
ja: "Peisteskin Treasure Map",
|
||||
de: "Peisteskin Treasure Map",
|
||||
fr: "Peisteskin Treasure Map",
|
||||
ja: "古ぼけた地図G5",
|
||||
de: "Basiliskenleder-Schatzkarte",
|
||||
fr: "Carte au trésor en peau de peiste",
|
||||
},
|
||||
7 => LocalisedText {
|
||||
en: "Leather Buried Treasure Map",
|
||||
ja: "Leather Buried Treasure Map",
|
||||
de: "Leather Buried Treasure Map",
|
||||
fr: "Leather Buried Treasure Map",
|
||||
ja: "隠された地図G1",
|
||||
de: "Kryptische Karte",
|
||||
fr: "Carte au trésor secrète en cuir",
|
||||
},
|
||||
8 => LocalisedText {
|
||||
en: "Archaeoskin Treasure Map",
|
||||
ja: "Archaeoskin Treasure Map",
|
||||
de: "Archaeoskin Treasure Map",
|
||||
fr: "Archaeoskin Treasure Map",
|
||||
ja: "古ぼけた地図G6",
|
||||
de: "Archaeoleder-Schatzkarte",
|
||||
fr: "Carte au trésor en peau d'archéornis",
|
||||
},
|
||||
9 => LocalisedText {
|
||||
en: "Wyvernskin Treasure Map",
|
||||
ja: "Wyvernskin Treasure Map",
|
||||
de: "Wyvernskin Treasure Map",
|
||||
fr: "Wyvernskin Treasure Map",
|
||||
ja: "古ぼけた地図G7",
|
||||
de: "Wyvernleder-Schatzkarte",
|
||||
fr: "Carte au trésor en peau de wyverne",
|
||||
},
|
||||
10 => LocalisedText {
|
||||
en: "Dragonskin Treasure Map",
|
||||
ja: "Dragonskin Treasure Map",
|
||||
de: "Dragonskin Treasure Map",
|
||||
fr: "Dragonskin Treasure Map",
|
||||
ja: "古ぼけた地図G8",
|
||||
de: "Drachenleder-Schatzkarte",
|
||||
fr: "Carte au trésor en peau de dragon",
|
||||
},
|
||||
11 => LocalisedText {
|
||||
en: "Gaganaskin Treasure Map",
|
||||
ja: "Gaganaskin Treasure Map",
|
||||
de: "Gaganaskin Treasure Map",
|
||||
fr: "Gaganaskin Treasure Map",
|
||||
ja: "古ぼけた地図G9",
|
||||
de: "Gaganaleder-Schatzkarte",
|
||||
fr: "Carte au trésor en peau de gagana",
|
||||
},
|
||||
12 => LocalisedText {
|
||||
en: "Gazelleskin Treasure Map",
|
||||
ja: "Gazelleskin Treasure Map",
|
||||
de: "Gazelleskin Treasure Map",
|
||||
fr: "Gazelleskin Treasure Map",
|
||||
ja: "古ぼけた地図G10",
|
||||
de: "Gazellenleder-Schatzkarte",
|
||||
fr: "Carte au trésor en peau de gazelle",
|
||||
},
|
||||
13 => LocalisedText {
|
||||
en: "Seemingly Special Treasure Map",
|
||||
ja: "Seemingly Special Treasure Map",
|
||||
de: "Seemingly Special Treasure Map",
|
||||
fr: "Seemingly Special Treasure Map",
|
||||
ja: "古ぼけた地図S1",
|
||||
de: "Exotenleder-Schatzkarte",
|
||||
fr: "Carte au trésor inhabituelle I",
|
||||
},
|
||||
14 => LocalisedText {
|
||||
en: "Gliderskin Treasure Map",
|
||||
ja: "Gliderskin Treasure Map",
|
||||
de: "Gliderskin Treasure Map",
|
||||
fr: "Gliderskin Treasure Map",
|
||||
ja: "古ぼけた地図G11",
|
||||
de: "Smilodonleder-Schatzkarte",
|
||||
fr: "Carte au trésor en peau de smilodon",
|
||||
},
|
||||
15 => LocalisedText {
|
||||
en: "Zonureskin Treasure Map",
|
||||
ja: "Zonureskin Treasure Map",
|
||||
de: "Zonureskin Treasure Map",
|
||||
fr: "Zonureskin Treasure Map",
|
||||
ja: "古ぼけた地図G12",
|
||||
de: "Glaucusleder-Schatzkarte",
|
||||
fr: "Carte au trésor en peau de glaucus",
|
||||
},
|
||||
16 => LocalisedText {
|
||||
en: "Presumably Special Treasure Map",
|
||||
ja: "Presumably Special Treasure Map",
|
||||
de: "Presumably Special Treasure Map",
|
||||
fr: "Presumably Special Treasure Map",
|
||||
ja: "古ぼけた地図S2",
|
||||
de: "Mythenleder-Schatzkarte",
|
||||
fr: "Carte au trésor inhabituelle II",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
use sestring::SeString;
|
||||
use crate::ffxiv::duties::{ContentKind, DutyInfo};
|
||||
use crate::ffxiv::{Language, LocalisedText};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq)]
|
||||
pub struct PartyFinderListing {
|
||||
|
@ -45,32 +46,57 @@ impl PartyFinderListing {
|
|||
self.search_area.contains(SearchAreaFlags::DATA_CENTRE)
|
||||
}
|
||||
|
||||
pub fn duty_name(&self, codes: &str) -> Cow<str> {
|
||||
pub fn duty_name(&self, lang: &Language) -> Cow<str> {
|
||||
match (&self.duty_type, &self.category) {
|
||||
(DutyType::Other, DutyCategory::Fates) => {
|
||||
if let Some(name) = crate::ffxiv::TERRITORY_NAMES.get(&u32::from(self.duty)) {
|
||||
return Cow::from(name.from_codes(codes));
|
||||
return Cow::from(name.text(lang));
|
||||
}
|
||||
|
||||
return Cow::from("Fates");
|
||||
return Cow::from("FATEs");
|
||||
}
|
||||
(DutyType::Other, DutyCategory::TheHunt) => return Cow::from("The Hunt"),
|
||||
(DutyType::Other, DutyCategory::Duty) if self.duty == 0 => return Cow::from("None"),
|
||||
(DutyType::Other, DutyCategory::DeepDungeons) if self.duty == 1 => return Cow::from("The Palace of the Dead"),
|
||||
(DutyType::Other, DutyCategory::DeepDungeons) if self.duty == 2 => return Cow::from("Heaven-on-High"),
|
||||
(DutyType::Other, DutyCategory::TheHunt) => return Cow::from(match lang {
|
||||
Language::English => "The Hunt",
|
||||
Language::Japanese => "モブハント",
|
||||
Language::German => "Hohe Jagd",
|
||||
Language::French => "Contrats de chasse",
|
||||
}),
|
||||
(DutyType::Other, DutyCategory::Duty) if self.duty == 0 => return Cow::from(match lang {
|
||||
Language::English => "None",
|
||||
Language::Japanese => "設定なし",
|
||||
Language::German => "Nicht festgelegt",
|
||||
Language::French => "Non spécifiée",
|
||||
}),
|
||||
(DutyType::Other, DutyCategory::DeepDungeons) if self.duty == 1 => return Cow::from(match lang {
|
||||
Language::English => "The Palace of the Dead",
|
||||
Language::Japanese => "死者の宮殿",
|
||||
Language::German => "Palast der Toten",
|
||||
Language::French => "Palais des morts",
|
||||
}),
|
||||
(DutyType::Other, DutyCategory::DeepDungeons) if self.duty == 2 => return Cow::from(match lang {
|
||||
Language::English => "Heaven-on-High",
|
||||
Language::Japanese => "アメノミハシラ",
|
||||
Language::German => "Himmelssäule",
|
||||
Language::French => "Pilier des Cieux",
|
||||
}),
|
||||
(DutyType::Normal, _) => {
|
||||
if let Some(info) = crate::ffxiv::DUTIES.get(&u32::from(self.duty)) {
|
||||
return Cow::from(info.name.from_codes(codes));
|
||||
return Cow::from(info.name.text(lang));
|
||||
}
|
||||
}
|
||||
(DutyType::Roulette, _) => {
|
||||
if let Some(info) = crate::ffxiv::ROULETTES.get(&u32::from(self.duty)) {
|
||||
return Cow::from(info.name.from_codes(codes));
|
||||
return Cow::from(info.name.text(lang));
|
||||
}
|
||||
}
|
||||
(_, DutyCategory::QuestBattles) => return Cow::from("Quest Battles"),
|
||||
(_, DutyCategory::QuestBattles) => return Cow::from(match lang {
|
||||
Language::English => "Quest Battles",
|
||||
Language::Japanese => "クエストバトル",
|
||||
Language::German => "Auftragskampf",
|
||||
Language::French => "Batailles de quête",
|
||||
}),
|
||||
(_, DutyCategory::TreasureHunt) => if let Some(name) = crate::ffxiv::TREASURE_MAPS.get(&u32::from(self.duty)) {
|
||||
return Cow::from(name.from_codes(codes));
|
||||
return Cow::from(name.text(lang));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -547,23 +573,98 @@ impl PartyFinderCategory {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn name(self) -> &'static str {
|
||||
pub fn name(self) -> LocalisedText {
|
||||
match self {
|
||||
Self::DutyRoulette => "Duty Roulette",
|
||||
Self::Dungeons => "Dungeons",
|
||||
Self::Guildhests => "Guildhests",
|
||||
Self::Trials => "Trials",
|
||||
Self::Raids => "Raids",
|
||||
Self::HighEndDuty => "High-end Duty",
|
||||
Self::Pvp => "PvP",
|
||||
Self::QuestBattles => "Quest Battles",
|
||||
Self::Fates => "FATEs",
|
||||
Self::TreasureHunt => "Treasure Hunt",
|
||||
Self::TheHunt => "The Hunt",
|
||||
Self::GatheringForays => "Gathering Forays",
|
||||
Self::DeepDungeons => "Deep Dungeons",
|
||||
Self::AdventuringForays => "Adventuring Forays",
|
||||
Self::None => "None",
|
||||
Self::DutyRoulette => LocalisedText {
|
||||
en: "Duty Roulette",
|
||||
ja: "コンテンツルーレット",
|
||||
de: "Zufallsinhalte",
|
||||
fr: "Missions aléatoires",
|
||||
},
|
||||
Self::Dungeons => LocalisedText {
|
||||
en: "Dungeons",
|
||||
ja: "ダンジョン",
|
||||
de: "Dungeons",
|
||||
fr: "Donjons",
|
||||
},
|
||||
Self::Guildhests => LocalisedText {
|
||||
en: "Guildhests",
|
||||
ja: "ギルドオーダー",
|
||||
de: "Gildengeheiße",
|
||||
fr: "Opérations de guilde",
|
||||
},
|
||||
Self::Trials => LocalisedText {
|
||||
en: "Trials",
|
||||
ja: "討伐・討滅戦",
|
||||
de: "Prüfungen",
|
||||
fr: "Défis",
|
||||
},
|
||||
Self::Raids => LocalisedText {
|
||||
en: "Raids",
|
||||
ja: "レイド",
|
||||
de: "Raids",
|
||||
fr: "Raids",
|
||||
},
|
||||
Self::HighEndDuty => LocalisedText {
|
||||
en: "High-end Duty",
|
||||
ja: "高難易度コンテンツ",
|
||||
de: "Schwierige Inhalte",
|
||||
fr: "Missions à difficulté élevée",
|
||||
},
|
||||
Self::Pvp => LocalisedText {
|
||||
en: "PvP",
|
||||
ja: "PvP",
|
||||
de: "PvP",
|
||||
fr: "JcJ",
|
||||
},
|
||||
Self::QuestBattles => LocalisedText {
|
||||
en: "Quest Battles",
|
||||
ja: "クエストバトル",
|
||||
de: "Auftragskampf",
|
||||
fr: "Batailles de quête",
|
||||
},
|
||||
Self::Fates => LocalisedText {
|
||||
en: "FATEs",
|
||||
ja: "F.A.T.E.",
|
||||
de: "FATEs",
|
||||
fr: "ALÉA",
|
||||
},
|
||||
Self::TreasureHunt => LocalisedText {
|
||||
en: "Treasure Hunt",
|
||||
ja: "トレジャーハント",
|
||||
de: "Schatzsuche",
|
||||
fr: "Chasse aux trésors",
|
||||
},
|
||||
Self::TheHunt => LocalisedText {
|
||||
en: "The Hunt",
|
||||
ja: "モブハント ",
|
||||
de: "Hohe Jagd",
|
||||
fr: "Contrats de chasse",
|
||||
},
|
||||
Self::GatheringForays => LocalisedText {
|
||||
en: "Gathering Forays",
|
||||
ja: "採集活動",
|
||||
de: "Sammeln",
|
||||
fr: "Récolte",
|
||||
},
|
||||
Self::DeepDungeons => LocalisedText {
|
||||
en: "Deep Dungeons",
|
||||
ja: "ディープダンジョン",
|
||||
de: "Tiefe Gewölbe",
|
||||
fr: "Donjons sans fond",
|
||||
},
|
||||
Self::AdventuringForays => LocalisedText {
|
||||
en: "Adventuring Forays",
|
||||
ja: "特殊フィールド探索",
|
||||
de: "Feldexkursion",
|
||||
fr: "Missions d'exploration",
|
||||
},
|
||||
Self::None => LocalisedText {
|
||||
en: "None",
|
||||
ja: "設定なし",
|
||||
de: "Nicht festgelegt",
|
||||
fr: "Non spécifiée",
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
use sestring::{Payload, SeString};
|
||||
use crate::ffxiv::Language;
|
||||
|
||||
pub trait SeStringExt {
|
||||
fn full_text(&self, codes: &str) -> String;
|
||||
fn full_text(&self, lang: &Language) -> String;
|
||||
}
|
||||
|
||||
impl SeStringExt for SeString {
|
||||
fn full_text(&self, codes: &str) -> String {
|
||||
fn full_text(&self, lang: &Language) -> String {
|
||||
self.0.iter()
|
||||
.flat_map(|payload| {
|
||||
match payload {
|
||||
Payload::Text(t) => Some(&*t.0),
|
||||
Payload::AutoTranslate(at) => crate::ffxiv::AUTO_TRANSLATE
|
||||
.get(&(u32::from(at.group), at.key))
|
||||
.map(|text| text.from_codes(codes)),
|
||||
.map(|text| text.text(lang)),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use askama::Template;
|
||||
use crate::listing_container::QueriedListing;
|
||||
use std::borrow::Borrow;
|
||||
use crate::ffxiv::Language;
|
||||
use crate::sestring_ext::SeStringExt;
|
||||
use crate::listing::PartyFinderCategory;
|
||||
|
||||
|
@ -8,5 +9,5 @@ use crate::listing::PartyFinderCategory;
|
|||
#[template(path = "listings.html")]
|
||||
pub struct ListingsTemplate {
|
||||
pub containers: Vec<QueriedListing>,
|
||||
pub codes: String,
|
||||
pub lang: Language,
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use warp::{Filter, Reply};
|
|||
use warp::filters::BoxedFilter;
|
||||
use warp::http::Uri;
|
||||
use crate::config::Config;
|
||||
use crate::ffxiv::Language;
|
||||
use crate::listing::PartyFinderListing;
|
||||
use crate::listing_container::{ListingContainer, QueriedListing};
|
||||
use crate::template::listings::ListingsTemplate;
|
||||
|
@ -124,7 +125,7 @@ fn index() -> BoxedFilter<(impl Reply, )> {
|
|||
fn listings(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
|
||||
async fn logic(state: Arc<State>, codes: Option<String>) -> std::result::Result<impl Reply, Infallible> {
|
||||
use mongodb::bson::doc;
|
||||
let codes = codes.unwrap_or_else(|| String::from("en"));
|
||||
let lang = Language::from_codes(codes.as_deref());
|
||||
|
||||
let res = state
|
||||
.collection()
|
||||
|
@ -186,14 +187,14 @@ fn listings(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
|
|||
|
||||
Ok(ListingsTemplate {
|
||||
containers,
|
||||
codes,
|
||||
lang,
|
||||
})
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("{:#?}", e);
|
||||
Ok(ListingsTemplate {
|
||||
containers: Default::default(),
|
||||
codes,
|
||||
lang,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -202,10 +203,13 @@ fn listings(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
|
|||
let route = warp::path("listings")
|
||||
.and(warp::path::end())
|
||||
.and(
|
||||
warp::filters::header::optional::<String>("accept-language")
|
||||
.or(warp::filters::cookie::optional::<String>("lang"))
|
||||
warp::cookie::<String>("lang")
|
||||
.or(warp::header::<String>("accept-language"))
|
||||
.unify()
|
||||
.map(Some)
|
||||
.or(warp::any().map(|| None))
|
||||
.unify()
|
||||
)
|
||||
.unify()
|
||||
.and_then(move |codes: Option<String>| logic(Arc::clone(&state), codes));
|
||||
|
||||
warp::get().and(route).boxed()
|
||||
|
|
|
@ -12,43 +12,53 @@ Remote Party Finder
|
|||
|
||||
{% block body %}
|
||||
<div id="container">
|
||||
<div class="requires-js">
|
||||
<input type="search" class="search" placeholder="Search"/>
|
||||
<select id="data-centre-filter">
|
||||
<option value="All">All</option>
|
||||
<optgroup label="North America">
|
||||
<option value="Aether">Aether</option>
|
||||
<option value="Crystal">Crystal</option>
|
||||
<option value="Primal">Primal</option>
|
||||
</optgroup>
|
||||
<optgroup label="Europe">
|
||||
<option value="Chaos">Chaos</option>
|
||||
<option value="Light">Light</option>
|
||||
</optgroup>
|
||||
<optgroup label="Japan">
|
||||
<option value="Elemental">Elemental</option>
|
||||
<option value="Gaia">Gaia</option>
|
||||
<option value="Mana">Mana</option>
|
||||
</optgroup>
|
||||
<optgroup label="Oceania">
|
||||
<option disabled value="">Not yet lmao</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<details class="filter-controls">
|
||||
<summary>Advanced</summary>
|
||||
<div>
|
||||
<div class="control">
|
||||
<label>
|
||||
Categories
|
||||
<select multiple id="category-filter">
|
||||
{%- for category in PartyFinderCategory::ALL %}
|
||||
<option value="{{ category.as_str() }}">{{ category.name() }}</option>
|
||||
{%- endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<div class="requires-js settings">
|
||||
<div class="left">
|
||||
<input type="search" class="search" placeholder="Search"/>
|
||||
<select id="data-centre-filter">
|
||||
<option value="All">All</option>
|
||||
<optgroup label="North America">
|
||||
<option value="Aether">Aether</option>
|
||||
<option value="Crystal">Crystal</option>
|
||||
<option value="Primal">Primal</option>
|
||||
</optgroup>
|
||||
<optgroup label="Europe">
|
||||
<option value="Chaos">Chaos</option>
|
||||
<option value="Light">Light</option>
|
||||
</optgroup>
|
||||
<optgroup label="Japan">
|
||||
<option value="Elemental">Elemental</option>
|
||||
<option value="Gaia">Gaia</option>
|
||||
<option value="Mana">Mana</option>
|
||||
</optgroup>
|
||||
<optgroup label="Oceania">
|
||||
<option disabled value="">Not yet lmao</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<details class="filter-controls">
|
||||
<summary>Advanced</summary>
|
||||
<div>
|
||||
<div class="control">
|
||||
<label>
|
||||
Categories
|
||||
<select multiple id="category-filter">
|
||||
{%- for category in PartyFinderCategory::ALL %}
|
||||
<option value="{{ category.as_str() }}">{{ category.name().text(lang) }}</option>
|
||||
{%- endfor %}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</details>
|
||||
</div>
|
||||
<div class="right">
|
||||
<select id="language">
|
||||
<option value="en">English</option>
|
||||
<option value="ja">日本語</option>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="fr">Français</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="listings" class="list">
|
||||
{%- if containers.is_empty() %}
|
||||
|
@ -68,9 +78,9 @@ Remote Party Finder
|
|||
{%- else %}
|
||||
{%- let duty_class = " local" %}
|
||||
{%- endif %}
|
||||
<div class="duty{{ duty_class }}">{{ listing.duty_name(codes.as_str()) }}</div>
|
||||
<div class="duty{{ duty_class }}">{{ listing.duty_name(lang) }}</div>
|
||||
<div class="description">
|
||||
{%- let desc = listing.description.full_text(codes.as_str()) %}
|
||||
{%- let desc = listing.description.full_text(lang) %}
|
||||
{%- if desc.trim().is_empty() -%}
|
||||
<em>None</em>
|
||||
{%- else -%}
|
||||
|
@ -120,7 +130,7 @@ Remote Party Finder
|
|||
</div>
|
||||
<div class="right meta">
|
||||
<div class="item creator">
|
||||
<span class="text">{{ listing.name.full_text(codes.as_str()) }} @ {{ listing.home_world_string() }}</span>
|
||||
<span class="text">{{ listing.name.full_text(lang) }} @ {{ listing.home_world_string() }}</span>
|
||||
<span title="Creator">
|
||||
<svg class="icon" viewBox="0 0 32 32">
|
||||
<use href="/assets/icons.svg#user"></use>
|
||||
|
|
Loading…
Reference in New Issue