Implement language translations for SplatNet data

This isn't detecting the language or downloading the additional language files yet.
This commit is contained in:
Matt Isenhower
2017-12-03 13:19:49 -08:00
parent db6d018ef5
commit 7e4fd8eb4a
16 changed files with 130 additions and 25 deletions

View File

@@ -51,6 +51,8 @@
"vue-loader": "^13.0.2",
"vue-router": "^3.0.1",
"vue-template-compiler": "^2.4.2",
"vuex": "^3.0.1",
"vuex-i18n": "^1.8.0",
"webpack": "^3.4.1",
"webpack-dev-server": "^2.6.1"
}

View File

@@ -10,8 +10,7 @@
</div>
<div class="level-item">
<h2 class="title is-3 is-size-2-fullhd is-size-4-mobile font-splatoon1">
<template v-if="isFestival">Splatfest Battle</template>
<template v-else>{{ gameMode.name }}</template>
<template>{{ name }}</template>
</h2>
</div>
</div>
@@ -25,6 +24,11 @@ export default {
isFestival() {
return this.festival && this.festival.times.start <= this.now && this.festival.times.end > this.now;
},
name() {
if (this.isFestival)
return 'Splatfest Battle';
return this.$t(`splatnet.game_modes.${this.gameMode.key}.name`, this.gameMode.name);
},
},
}
</script>

View File

@@ -2,13 +2,13 @@
<div class="merchandise-box font-splatoon2" :class="merchandise.kind">
<div class="brand">
<div class="image is-32x32">
<img :src="merchandise.gear.brand.image | localSplatNetImageUrl" :title="merchandise.gear.brand.name" />
<img :src="merchandise.gear.brand.image | localSplatNetImageUrl" :title="brandName" />
</div>
</div>
<div class="skills">
<div class="main skill-img-bg" v-if="merchandise.skill">
<img :src="merchandise.skill.image | localSplatNetImageUrl" :title="merchandise.skill.name" />
<img :src="merchandise.skill.image | localSplatNetImageUrl" :title="skillName" />
</div>
<div class="sub" v-for="i in merchandise.gear.rarity + 1">
<img src="~@/img/blank-skill-slot.png" />
@@ -26,7 +26,7 @@
</div>
<div class="gear-name has-text-centered">
{{ merchandise.gear.name }}
{{ gearName }}
<div class="info-overlay">
<div class="info-overlay-container original-gear" v-if="merchandise.original_gear">
@@ -36,7 +36,7 @@
<div class="level-left">
<div class="level-item">
<div class="skill-img-bg strikethrough">
<img :src="merchandise.original_gear.skill.image | localSplatNetImageUrl" :title="merchandise.original_gear.skill.name" />
<img :src="merchandise.original_gear.skill.image | localSplatNetImageUrl" :title="originalSkillName" />
</div>
<div class="sub" v-for="i in merchandise.original_gear.rarity + 1">
<img src="~@/img/blank-skill-slot.png" />
@@ -55,10 +55,10 @@
</div>
<div class="info-overlay-container common-ability" v-if="merchandise.gear.brand.frequent_skill">
<div class="is-size-7">{{ merchandise.gear.brand.name }}</div>
<div class="is-size-7">{{ brandName }}</div>
<div>
<div class="skill-img-bg">
<img :src="merchandise.gear.brand.frequent_skill.image | localSplatNetImageUrl" :title="merchandise.gear.brand.frequent_skill.name" />
<img :src="merchandise.gear.brand.frequent_skill.image | localSplatNetImageUrl" :title="frequentSkillName" />
</div>
Common Ability
</div>
@@ -76,5 +76,28 @@
<script>
export default {
props: ['merchandise', 'now'],
computed: {
brandName() {
let brand = this.merchandise.gear.brand;
return this.$t(`splatnet.brands.${brand.id}.name`, brand.name);
},
skillName() {
let skill = this.merchandise.skill;
return this.$t(`splatnet.skills.${skill.id}.name`, skill.name);
},
originalSkillName() {
let originalGear = this.merchandise.original_gear;
if (originalGear)
return this.$t(`splatnet.skills.${originalGear.skill.id}.name`, originalGear.skill.name);
},
frequentSkillName() {
let frequentSkill = this.merchandise.gear.brand.frequent_skill;
return this.$t(`splatnet.skills.${frequentSkill.id}.name`, frequentSkill.name);
},
gearName() {
let gear = this.merchandise.gear;
return this.$t(`splatnet.gear.${gear.id}.name`, gear.name);
},
},
}
</script>

View File

@@ -7,13 +7,13 @@
<div class="main-content-wrapper">
<div class="weapon-special-sub">
<div class="image-wrapper" :title="`Special: ${weapon.special.name}`">
<div class="image-wrapper" :title="specialName">
<div class="image is-24x24">
<img :src="weapon.special.image_a | localSplatNetImageUrl" />
</div>
</div>
<div class="image-wrapper" :title="`Sub: ${weapon.sub.name}`">
<div class="image-wrapper" :title="subName">
<div class="image is-24x24">
<img :src="weapon.sub.image_a | localSplatNetImageUrl" />
</div>
@@ -25,7 +25,7 @@
</div>
<div class="weapon-name has-text-centered is-size-5">
{{ weapon.name }}
{{ name }}
</div>
</div>
@@ -38,5 +38,18 @@ export default {
weapon: {},
title: { default: 'New Weapon' },
},
computed: {
name() {
return this.$t(`splatnet.weapons.${this.weapon.id}.name`, this.weapon.name);
},
subName() {
let sub = this.weapon.sub;
return 'Sub: ' + this.$t(`splatnet.weapon_subs.${sub.id}.name`, sub.name);
},
specialName() {
let special = this.weapon.special;
return 'Special: ' + this.$t(`splatnet.weapon_specials.${special.id}.name`, special.name);
},
}
}
</script>

View File

@@ -25,7 +25,7 @@
<div class="salmon-run-gear-text">
This month's gear
</div>
<div class="salmon-run-gear-image hand" @click="gearDialogOpen = true" :title="coop.reward_gear.gear.name">
<div class="salmon-run-gear-image hand" @click="gearDialogOpen = true" :title="rewardGearName">
<div class="image is-32x32">
<img :src="coop.reward_gear.gear.image | localSplatNetImageUrl" />
</div>
@@ -139,6 +139,12 @@ export default {
if (this.allSchedules.length > 0 && this.allSchedules[0].start_time <= this.now)
return this.allSchedules[0];
},
rewardGearName() {
if (this.coop.reward_gear) {
let gear = this.coop.reward_gear.gear;
return this.$t(`splatnet.gear.${gear.id}.name`, gear.name);
}
},
},
}
</script>

View File

@@ -1,11 +1,11 @@
<template>
<div class="columns is-mobile is-slimmer" style="padding: 0.5rem 0" v-if="schedule.stage || schedule.weapons">
<div class="column" :class="mini ? 'is-3' : 'is-5'" v-if="schedule.stage">
<Stage :stage="schedule.stage" :showTitle="!mini"></Stage>
<Stage :stage="schedule.stage" :isSalmonRun="true" :showTitle="!mini"></Stage>
</div>
<div class="column" style="display: flex; align-items: center;" v-if="mini && schedule.stage">
{{ schedule.stage.name }}
{{ stageName }}
</div>
<div class="column" :class="{ 'is-5': mini }" style="display: flex; align-items: center;" v-if="schedule.weapons">
@@ -15,7 +15,7 @@
<div class="columns is-mobile is-slimmer">
<div class="column" v-for="weapon in schedule.weapons">
<div class="image is-square weapon">
<img :src="weapon.image | localSplatNetImageUrl" :title="weapon.name" v-if="weapon" />
<img :src="weapon.image | localSplatNetImageUrl" :title="$t(`splatnet.weapons.${weapon.id}.name`, weapon.name)" v-if="weapon" />
<img src="~@/img/salmon-run-random-weapon.png" title="Random" style="padding: 8px" v-else />
</div>
</div>
@@ -31,5 +31,11 @@ import Stage from './Stage.vue';
export default {
components: { Stage },
props: ['schedule', 'mini'],
computed: {
stageName() {
let stage = this.schedule.stage;
return this.$t(`splatnet.coop_stages.${stage.image}.name`, stage.name);
},
},
}
</script>

View File

@@ -5,7 +5,7 @@
<div class="main-schedule" v-if="currentSchedule">
<div class="level is-mobile top-bar">
<div class="level-left">
<div class="level-item title-color is-size-5">{{ currentSchedule.rule.name }}</div>
<div class="level-item title-color is-size-5">{{ currentRuleName }}</div>
</div>
<div class="level-right">
<div class="level-item">
@@ -79,6 +79,10 @@ export default {
if (this.schedules && this.schedules[0] && this.schedules[0].start_time <= this.now)
return this.schedules[0];
},
currentRuleName() {
let rule = this.currentSchedule.rule;
return this.$t(`splatnet.rules.${rule.key}.name`, rule.name);
},
upcomingSchedules() {
if (this.schedules) {
if (this.currentSchedule)

View File

@@ -2,7 +2,7 @@
<div>
<div class="level is-mobile is-marginless is-hidden-tablet">
<div class="level-left">
<div class="level-item title-color is-size-5">{{ schedule.rule.name }}</div>
<div class="level-item title-color is-size-5">{{ ruleName }}</div>
</div>
<div class="level-right">
<div class="level-item has-text-right">
@@ -21,7 +21,7 @@
<div class="columns is-slim" v-if="schedule">
<div class="column has-text-centered is-hidden-mobile" style="margin-top: auto; margin-bottom: auto;">
<div class="title is-size-5-fullhd is-size-5-touch" :class="ruleNameClass">{{ schedule.rule.name }}</div>
<div class="title is-size-5-fullhd is-size-5-touch" :class="ruleNameClass">{{ ruleName }}</div>
<div class="subtitle is-size-6-fullhd is-size-6-touch" :class="scheduleClass">
<template v-if="schedule.start_time > now">
in {{ schedule.start_time - now | duration }}
@@ -57,6 +57,10 @@ export default {
let start = new Date(this.schedule.start_time * 1000);
return (now.getDate() == start.getDate() && now.getMonth() == start.getMonth() && now.getFullYear() == start.getFullYear());
},
ruleName() {
let rule = this.schedule.rule;
return this.$t(`splatnet.rules.${rule.key}.name`, rule.name);
},
ruleNameClass() { return this.smallSize ? 'is-6' : 'is-5' },
scheduleClass() { return this.smallSize ? 'is-7' : 'is-6' },
}

View File

@@ -13,11 +13,11 @@
<div class="labels">
<div class="alpha">
{{ festival.names.alpha_short }}
{{ $t(`splatnet.festivals.${festival.festival_id}.names.alpha_short`, festival.names.alpha_short) }}
</div>
<div class="bravo">
{{ festival.names.bravo_short }}
{{ $t(`splatnet.festivals.${festival.festival_id}.names.bravo_short`, festival.names.bravo_short) }}
</div>
</div>

View File

@@ -6,7 +6,7 @@
<div class="title is-4 font-splatoon1 has-text-centered is-marginless results-header-text">Results</div>
<div class="column">
<div class="image is-48x48" :title="festival.names.alpha_short">
<div class="image is-48x48" :title="$t(`splatnet.festivals.${festival.festival_id}.names.alpha_short`, festival.names.alpha_short)">
<img :src="festival.images.alpha | localSplatNetImageUrl" />
</div>
</div>
@@ -16,7 +16,7 @@
</div>
<div class="column">
<div class="image is-48x48" :title="festival.names.bravo_short">
<div class="image is-48x48" :title="$t(`splatnet.festivals.${festival.festival_id}.names.bravo_short`, festival.names.bravo_short)">
<img :src="festival.images.bravo | localSplatNetImageUrl" />
</div>
</div>

View File

@@ -1,7 +1,7 @@
<template>
<div class="stage-image" :class="{ hand: clickable }" :style="style" @click="click" v-if="stageDetails">
<figure class="image is-16by9"></figure>
<span class="stage-title" v-if="showTitle">{{ stageDetails.name }}</span>
<span class="stage-title" v-if="showTitle">{{ name }}</span>
<Modal v-if="isOpen" @close="isOpen = false">
<div class="modal-content tilt-right-slight is-wide">
@@ -10,7 +10,7 @@
</p>
<p class="has-text-centered">
<span class="font-splatoon2">
{{ stageDetails.name }}
{{ name }}
</span>
</p>
</div>
@@ -35,6 +35,7 @@ export default {
stage: {},
showTitle: { default: true },
clickable: { default: true },
isSalmonRun: { default: false },
},
data() {
return {
@@ -48,6 +49,12 @@ export default {
return Object.assign({}, SplatoonStages.find(stage => stage.id === this.stage.id), this.stage);
}
},
name() {
if (this.isSalmonRun)
return this.$t(`splatnet.coop_stages.${this.stage.image}.name`, this.stage.name);
return this.$t(`splatnet.stages.${this.stageDetails.id}.name`, this.stageDetails.name);
},
image() {
if (this.stageDetails)
return Vue.filter('localSplatNetImageUrl')(this.stageDetails.image);

8
src/js/i18n/index.js Normal file
View File

@@ -0,0 +1,8 @@
import Vue from 'vue';
import VuexI18n from 'vuex-i18n';
import store from '../store';
Vue.use(VuexI18n.plugin, store);
Vue.i18n.set('en');
Vue.i18n.fallback('en');

View File

@@ -1,10 +1,16 @@
import Vue from 'vue';
import store from './store';
import './i18n';
import './directives.js';
import './filters.js';
// Start the Vue app with our root component
import App from './components/App.vue';
new Vue(App).$mount('#app');
new Vue({
render: h => h(App),
store,
}).$mount('#app');
// Start analytics
require('./analytics');

View File

@@ -1,6 +1,9 @@
// Entry point for the automated screenshots page
import Vue from 'vue';
import store from './store';
import './i18n';
import './directives.js';
import './filters.js';
@@ -27,6 +30,7 @@ const router = new VueRouter({ routes });
import Screenshots from './components/Screenshots.vue';
new Vue({
render: h => h(Screenshots),
store,
router,
}).$mount('#app');

10
src/js/store/index.js Normal file
View File

@@ -0,0 +1,10 @@
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const debug = process.env.NODE_ENV !== 'production';
export default new Vuex.Store({
strict: debug,
});

View File

@@ -5426,6 +5426,14 @@ vue@^2.4.2:
version "2.5.6"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.6.tgz#73654fefa4b37f25dfc657b8b834b44c90822cd7"
vuex-i18n@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/vuex-i18n/-/vuex-i18n-1.8.0.tgz#9a13983fd6ed88f1d8510831364b230083c1df72"
vuex@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.0.1.tgz#e761352ebe0af537d4bb755a9b9dc4be3df7efd2"
watchpack@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.4.0.tgz#4a1472bcbb952bd0a9bb4036801f954dfb39faac"