mirror of
https://github.com/misenhower/splatoon3.ink.git
synced 2026-03-21 17:54:13 -05:00
Retrieve localized strings from SplatNet
This commit is contained in:
parent
d5560477a2
commit
47d0abb747
|
|
@ -1,3 +1,5 @@
|
|||
import path from 'path';
|
||||
|
||||
export function getTopOfCurrentHour(date = null) {
|
||||
date ??= new Date;
|
||||
|
||||
|
|
@ -15,3 +17,16 @@ export function getGearIcon(gear) {
|
|||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function deriveId(node) {
|
||||
// Unfortunately, SplatNet doesn't return IDs for a lot of gear properties.
|
||||
// Derive IDs from image URLs instead.
|
||||
|
||||
let url = new URL(node.image.url);
|
||||
let id =path.basename(url.pathname, '.png');
|
||||
|
||||
return {
|
||||
'__splatoon3ink_id': id,
|
||||
...node,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import mkdirp from 'mkdirp';
|
||||
import jsonpath from 'jsonpath';
|
||||
import get from 'lodash/get.js';
|
||||
import set from 'lodash/set.js';
|
||||
|
||||
function makeArray(value) {
|
||||
return Array.isArray(value) ? value : [value];
|
||||
}
|
||||
|
||||
export class LocalizationProcessor {
|
||||
outputDirectory = 'dist/data/locale';
|
||||
|
||||
constructor(locale, rulesets) {
|
||||
this.locale = locale;
|
||||
this.rulesets = rulesets;
|
||||
}
|
||||
|
||||
get filename() {
|
||||
return `${this.outputDirectory}/${this.locale.code}.json`;
|
||||
}
|
||||
|
||||
*rulesetIterations() {
|
||||
for (let ruleset of this.rulesets) {
|
||||
for (let node of makeArray(ruleset.nodes)) {
|
||||
for (let value of makeArray(ruleset.values)) {
|
||||
yield {
|
||||
key: ruleset.key,
|
||||
node,
|
||||
id: ruleset.id,
|
||||
value,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*dataIterations(data) {
|
||||
for (let ruleset of this.rulesetIterations()) {
|
||||
for (let node of jsonpath.query(data, ruleset.node)) {
|
||||
let id = get(node, ruleset.id);
|
||||
|
||||
yield {
|
||||
ruleset,
|
||||
node,
|
||||
id,
|
||||
value: get(node, ruleset.value),
|
||||
path: `${ruleset.key}.${id}.${ruleset.value}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async updateLocalizations(data) {
|
||||
let localizations = await this.readData();
|
||||
|
||||
for (let { path, value } of this.dataIterations(data)) {
|
||||
set(localizations, path, value);
|
||||
}
|
||||
|
||||
await this.writeData(localizations);
|
||||
}
|
||||
|
||||
async hasMissingLocalizations(data) {
|
||||
let localizations = await this.readData();
|
||||
|
||||
for (let { path } of this.dataIterations(data)) {
|
||||
if (get(localizations, path) === undefined) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async writeData(data) {
|
||||
// If we're running in debug mode, format the JSON output so it's easier to read
|
||||
let debug = !!process.env.DEBUG;
|
||||
let space = debug ? 2 : undefined;
|
||||
|
||||
data = JSON.stringify(data, undefined, space);
|
||||
|
||||
await mkdirp(path.dirname(this.filename))
|
||||
await fs.writeFile(this.filename, data);
|
||||
}
|
||||
|
||||
async readData() {
|
||||
try {
|
||||
let result = await fs.readFile(this.filename);
|
||||
|
||||
return JSON.parse(result) || {};
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import jsonpath from 'jsonpath';
|
||||
import { deriveId } from "../../common/util.mjs";
|
||||
import DataUpdater from "./DataUpdater.mjs";
|
||||
|
||||
export default class CoopUpdater extends DataUpdater
|
||||
|
|
@ -9,7 +11,20 @@ export default class CoopUpdater extends DataUpdater
|
|||
'$..image.url',
|
||||
];
|
||||
|
||||
getData(locale) {
|
||||
return this.splatnet(locale).getCoopHistoryData();
|
||||
localizations = [
|
||||
{
|
||||
key: 'gear',
|
||||
nodes: '$..monthlyGear',
|
||||
id: '__splatoon3ink_id',
|
||||
values: 'name',
|
||||
},
|
||||
];
|
||||
|
||||
async getData(locale) {
|
||||
let data = await this.splatnet(locale).getCoopHistoryData();
|
||||
|
||||
jsonpath.apply(data, '$..monthlyGear', deriveId);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import SplatNet3Client from "../../splatnet/SplatNet3Client.mjs";
|
|||
import ImageProcessor from '../ImageProcessor.mjs';
|
||||
import NsoClient from '../../splatnet/NsoClient.mjs';
|
||||
import { locales } from '../../../src/common/i18n.mjs';
|
||||
import { LocalizationProcessor } from '../LocalizationProcessor.mjs';
|
||||
|
||||
export default class DataUpdater
|
||||
{
|
||||
|
|
@ -15,6 +16,7 @@ export default class DataUpdater
|
|||
outputDirectory = 'dist/data';
|
||||
|
||||
imagePaths = [];
|
||||
localizations = [];
|
||||
|
||||
constructor(region = null) {
|
||||
this.nsoClient = NsoClient.make(region);
|
||||
|
|
@ -52,6 +54,9 @@ export default class DataUpdater
|
|||
// Retrieve the data
|
||||
let data = await this.tryRequest(this.getData(this.defaultLocale));
|
||||
|
||||
// Update localizations
|
||||
await this.updateLocalizations(this.defaultLocale, data);
|
||||
|
||||
// Download any new images
|
||||
await this.downloadImages(data);
|
||||
|
||||
|
|
@ -79,6 +84,24 @@ export default class DataUpdater
|
|||
|
||||
// Processing
|
||||
|
||||
async updateLocalizations(initialLocale, data) {
|
||||
// Save localizations for the initial locale
|
||||
let processor = new LocalizationProcessor(initialLocale, this.localizations);
|
||||
await processor.updateLocalizations(data);
|
||||
|
||||
// Retrieve data for missing languages
|
||||
for (let locale of this.locales.filter(l => l !== initialLocale)) {
|
||||
processor = new LocalizationProcessor(locale, this.localizations);
|
||||
|
||||
if (await processor.hasMissingLocalizations(data)) {
|
||||
this.console.info(`Retrieving localized data for ${locale.code}`);
|
||||
|
||||
let regionalData = await this.getData(locale);
|
||||
await processor.updateLocalizations(regionalData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async downloadImages(data) {
|
||||
for (let expression of this.imagePaths) {
|
||||
// This JSONPath library is completely synchronous, so we have to
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import DataUpdater from "./DataUpdater.mjs";
|
||||
import jsonpath from 'jsonpath';
|
||||
import { deriveId } from "../../common/util.mjs";
|
||||
|
||||
export default class GearUpdater extends DataUpdater
|
||||
{
|
||||
|
|
@ -9,7 +11,39 @@ export default class GearUpdater extends DataUpdater
|
|||
'$..image.url',
|
||||
];
|
||||
|
||||
getData(locale) {
|
||||
return this.splatnet(locale).getGesotownData();
|
||||
localizations = [
|
||||
{
|
||||
key: 'brands',
|
||||
nodes: '$..brand',
|
||||
id: 'id',
|
||||
values: 'name',
|
||||
},
|
||||
{
|
||||
key: 'gear',
|
||||
nodes: '$..gear',
|
||||
id: '__splatoon3ink_id',
|
||||
values: 'name',
|
||||
},
|
||||
{
|
||||
key: 'powers',
|
||||
nodes: [
|
||||
'$..usualGearPower',
|
||||
'$..primaryGearPower',
|
||||
'$..additionalGearPowers.*',
|
||||
],
|
||||
id: '__splatoon3ink_id',
|
||||
values: 'name',
|
||||
},
|
||||
];
|
||||
|
||||
async getData(locale) {
|
||||
let data = await this.splatnet(locale).getGesotownData();
|
||||
|
||||
jsonpath.apply(data, '$..gear', deriveId);
|
||||
jsonpath.apply(data, '$..usualGearPower', deriveId);
|
||||
jsonpath.apply(data, '$..primaryGearPower', deriveId);
|
||||
jsonpath.apply(data, '$..additionalGearPowers.*', deriveId);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import DataUpdater from "./DataUpdater.mjs";
|
||||
import jsonpath from 'jsonpath';
|
||||
import { deriveId } from "../../common/util.mjs";
|
||||
|
||||
export default class StageScheduleUpdater extends DataUpdater
|
||||
{
|
||||
|
|
@ -11,7 +13,32 @@ export default class StageScheduleUpdater extends DataUpdater
|
|||
'$..thumbnailImage.url',
|
||||
];
|
||||
|
||||
getData(locale) {
|
||||
return this.splatnet(locale).getStageScheduleData();
|
||||
localizations = [
|
||||
{
|
||||
key: 'stages',
|
||||
nodes: '$..vsStages.nodes.*',
|
||||
id: 'id',
|
||||
values: 'name',
|
||||
},
|
||||
{
|
||||
key: 'rules',
|
||||
nodes: '$..vsRule',
|
||||
id: 'id',
|
||||
values: 'name',
|
||||
},
|
||||
{
|
||||
key: 'weapons',
|
||||
nodes: '$..weapons.*',
|
||||
id: '__splatoon3ink_id',
|
||||
values: 'name',
|
||||
},
|
||||
];
|
||||
|
||||
async getData(locale) {
|
||||
let data = await this.splatnet(locale).getStageScheduleData();
|
||||
|
||||
jsonpath.apply(data, '$..weapons.*', deriveId);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
package-lock.json
generated
1
package-lock.json
generated
|
|
@ -14,6 +14,7 @@
|
|||
"dotenv": "^16.0.2",
|
||||
"ecstatic": "^4.1.4",
|
||||
"jsonpath": "^1.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mkdirp": "^1.0.4",
|
||||
"nxapi": "^1.4.0",
|
||||
"pinia": "^2.0.22",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
"dotenv": "^16.0.2",
|
||||
"ecstatic": "^4.1.4",
|
||||
"jsonpath": "^1.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mkdirp": "^1.0.4",
|
||||
"nxapi": "^1.4.0",
|
||||
"pinia": "^2.0.22",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user