0722-editor-ja-lang

This commit is contained in:
feie9454 2025-07-22 16:51:20 +08:00
parent 6ae842176f
commit 298d1bdc7b
40 changed files with 139 additions and 49 deletions

View File

@ -1,5 +1,5 @@
<!doctype html> <!doctype html>
<html lang="en"> <html>
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />

View File

@ -5,9 +5,10 @@ import imgs from './assets/imgs'
import { DimensionColors, DimensionName, findAllTraitByDimension, getRandomDimensionRating, getRandomTraitRating, TraitName } from './config'; import { DimensionColors, DimensionName, findAllTraitByDimension, getRandomDimensionRating, getRandomTraitRating, TraitName } from './config';
import Game from './views/Game.vue'; import Game from './views/Game.vue';
import { computed, Ref, ref } from 'vue'; import { computed, Ref, ref, watch } from 'vue';
import Result from './views/Result.vue'; import Result from './views/Result.vue';
import { useI18n } from 'vue-i18n'
import { setHtmlLang } from './utils'
const pIndex = ref(0) const pIndex = ref(0)
const result: Ref<Record<DimensionName, number>> = ref(getRandomDimensionRating()) const result: Ref<Record<DimensionName, number>> = ref(getRandomDimensionRating())
@ -30,10 +31,16 @@ function finishGame(r: Record<DimensionName, number>, t: Record<TraitName, numbe
trait.value = t trait.value = t
} }
import lang from './locates' import lang, { MessageSchema } from './locates'
import Report from './views/Report.vue'; import Report from './views/Report.vue';
import PersonalInfo from './views/PersonalInfo.vue'; import PersonalInfo from './views/PersonalInfo.vue';
import ReportMask from './views/ReportMask.vue'; import ReportMask from './views/ReportMask.vue';
const { locale } = useI18n<{ message: MessageSchema }>({ useScope: 'global' })
// localeHTML lang
watch(locale, (newLocale) => {
setHtmlLang(newLocale)
}, { immediate: true })
const GraphDimensions = computed(() => { const GraphDimensions = computed(() => {
const average = Object.values(result.value).reduce((acc, cur) => acc + cur, 0) / 5; const average = Object.values(result.value).reduce((acc, cur) => acc + cur, 0) / 5;
@ -43,8 +50,12 @@ const GraphDimensions = computed(() => {
const newScores = Object.entries(result.value).map(([dimName, score]) => { const newScores = Object.entries(result.value).map(([dimName, score]) => {
const mean = average const mean = average
const newScore = Math.max(8, score + sign(score - mean) * Math.abs(score - mean) ** p) const newScore = Math.max(8, score + sign(score - mean) * Math.abs(score - mean) ** p)
const currentLang = lang[locale.value as keyof typeof lang] ?? lang.en
return { return {
name_zh: lang.zh_CN.questionData.DimensionName[dimName as DimensionName],
name_zh: currentLang.questionData.DimensionName[dimName as DimensionName],
name_en: lang.en.questionData.DimensionName[dimName as DimensionName], name_en: lang.en.questionData.DimensionName[dimName as DimensionName],
value: newScore, value: newScore,
color: DimensionColors[dimName as DimensionName], color: DimensionColors[dimName as DimensionName],
@ -85,7 +96,7 @@ const topFiveTraits = computed(() => {
const gender = ref('_gender_') const gender = ref('_gender_')
const username = ref('_username_') const username = ref('_userna_')
const age = ref(0) const age = ref(0)
function submitUserInfo(data: { gender: string, username: string, age: number }) { function submitUserInfo(data: { gender: string, username: string, age: number }) {
@ -95,18 +106,24 @@ function submitUserInfo(data: { gender: string, username: string, age: number })
pIndex.value = 3 pIndex.value = 3
} }
const showDebug = ref(1) const showDebug = ref(-5)
</script> </script>
<template> <template>
<div class="main"> <div class="main">
<div class="debug" v-if="showDebug > 0"> <div class="debug" v-if="showDebug > 0">
<span>Debug {{ showDebug }}</span>
<button v-for="i in [0, 1, 2, 3, 4, 5]" @click="pIndex = i">qIndex = {{ i <button v-for="i in [0, 1, 2, 3, 4, 5]" @click="pIndex = i">qIndex = {{ i
}}</button> }}</button>
<button <button
@click="result = getRandomDimensionRating(); trait = getRandomTraitRating()">RandomResult</button> @click="result = getRandomDimensionRating(); trait = getRandomTraitRating()">RandomResult</button>
<button @click="$i18n.locale = $i18n.locale === 'en' ? 'zh' : 'en'">Lang: <span>lang: {{ $i18n.locale }}</span>
{{ $i18n.locale }}</button> <button @click="$i18n.locale = 'zh_CN'">zh_CN </button>
<button @click="$i18n.locale = 'zh_TW'">zh_TW</button>
<button @click="$i18n.locale = 'en'">en</button>
<button @click="$i18n.locale = 'ja'">ja</button>
<button @click="$i18n.locale = 'ko'">ko</button>
</div> </div>
<TransitionGroup name="fade"> <TransitionGroup name="fade">
<Home key="home" v-if="pIndex == 0" @next="pIndex = 1" /> <Home key="home" v-if="pIndex == 0" @next="pIndex = 1" />
@ -119,10 +136,12 @@ const showDebug = ref(1)
:graph="GraphDimensions" @retry="resetGame" :rarity="rarity" /> :graph="GraphDimensions" @retry="resetGame" :rarity="rarity" />
<Report key="report" v-if="pIndex == 4" :result="result" :trait="trait" <Report key="report" v-if="pIndex == 4" :result="result" :trait="trait"
:top-five-traits="topFiveTraits" :username="username" :top-five-traits="topFiveTraits" :username="username"
:graph="GraphDimensions" @retry="resetGame" :rarity="rarity" @next="pIndex++" /> :graph="GraphDimensions" @retry="resetGame" :rarity="rarity"
<ReportMask key="report-mask" v-if="pIndex == 5" :result="result" :trait="trait" @next="pIndex++" />
:top-five-traits="topFiveTraits" :username="username" <ReportMask key="report-mask" v-if="pIndex == 5" :result="result"
:graph="GraphDimensions" @retry="resetGame" :rarity="rarity"@back="pIndex--" /> :trait="trait" :top-five-traits="topFiveTraits" :username="username"
:graph="GraphDimensions" @retry="resetGame" :rarity="rarity"
@back="pIndex--" />
</TransitionGroup> </TransitionGroup>
<img :src="imgs.logo" alt="" class="logo" @click="showDebug++" <img :src="imgs.logo" alt="" class="logo" @click="showDebug++"
v-if="pIndex == 0 || pIndex == 1 || pIndex == 2" v-if="pIndex == 0 || pIndex == 1 || pIndex == 2"

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -106,7 +106,11 @@ defineProps<{
font-size: 0.9rem; font-size: 0.9rem;
} }
} }
&:lang(ja) ,&:lang(ko){
.content {
font-size: 0.7rem;
}
}
span { span {
height: 0; height: 0;
display: block; display: block;

View File

@ -558,7 +558,6 @@ const ko: typeof zh_CN = {
button: new URL("@/assets/imgs/home/button_ko.png", import.meta.url).href, button: new URL("@/assets/imgs/home/button_ko.png", import.meta.url).href,
intro: new URL("@/assets/imgs/home/intro_ko.png", import.meta.url).href, intro: new URL("@/assets/imgs/home/intro_ko.png", import.meta.url).href,
}, },
// 以下為您提供的譯文,已修正格式
game: { game: {
agree: "매우 그렇다", agree: "매우 그렇다",
disagree: "매우 그렇지 않다", disagree: "매우 그렇지 않다",
@ -622,7 +621,7 @@ const ko: typeof zh_CN = {
"Emotion_Management_Master": "감정기복 ZERO", "Emotion_Management_Master": "감정기복 ZERO",
"Future_Prophet": "미래 각도기", "Future_Prophet": "미래 각도기",
"Helmsman": "8톤트럭 운전수", "Helmsman": "항해사", // Original had "8톤트럭 운전수", but source text had "항해사"
"Owner": "책임감 MAX", "Owner": "책임감 MAX",
"Strategic_Leader": "전략가 타입", "Strategic_Leader": "전략가 타입",
"Perfectionist": "완벽주의 ON", "Perfectionist": "완벽주의 ON",
@ -645,12 +644,12 @@ const ko: typeof zh_CN = {
male: "남성", male: "남성",
female: "여성", female: "여성",
}, },
error: { // 參照 placeholder 補全 error: {
username: "닉네임을 입력하세요", username: "닉네임을 입력하세요",
age: "나이를 입력하세요", age: "나이를 입력하세요",
gender: "성별을 선택하세요", gender: "성별을 선택하세요",
}, },
submit: new URL("@/assets/imgs/personalInfo/submit_ko.png", import.meta.url).href, submit: new URL("@/assets/imgs/personalInfo/submit_en.png", import.meta.url).href,
}, },
result: { result: {
title: "당신의 다섯가지 잠재력 (5 Potentials) 점수", title: "당신의 다섯가지 잠재력 (5 Potentials) 점수",
@ -674,25 +673,31 @@ const ko: typeof zh_CN = {
report: { report: {
title: "님의 핵심 잠재력은", title: "님의 핵심 잠재력은",
dimDescription: { dimDescription: {
Learning_Agility: dedent` Learning_Agility: `
, . , . , . , .
. , . , . . , . , .
`, `,
Resilience: dedent` Resilience: `
, . . , . .
, . , 믿 . , . , 믿 .
`, `,
Empathy: dedent` Empathy: `
, . '마인드 헌터', . , . '마인드 헌터', .
, . , . 믿. , . , . 믿.
`, `,
Ambition: dedent` Ambition: `
, '감히 도전하고 한계를 뛰어넘는' . , '감히 도전하고 한계를 뛰어넘는' .
, . , .
, . , . , 믿. , . , . , 믿.
`, `,
Judgement: dedent` Judgement: `
, . . , . .
, . , . , . , .
` `
}, },
@ -823,22 +828,27 @@ const ja: typeof zh_CN = {
dimDescription: { dimDescription: {
Learning_Agility: dedent` Learning_Agility: dedent`
`, `,
Resilience: dedent` Resilience: dedent`
姿 姿
姿 姿
`, `,
Empathy: dedent` Empathy: dedent`
`, `,
Ambition: dedent` Ambition: dedent`
`, `,
Judgement: dedent` Judgement: dedent`
` `
}, },

View File

@ -3,6 +3,7 @@ import './assets/fonts/stylesheet.css'
import './style.scss' import './style.scss'
import App from './App.vue' import App from './App.vue'
import { createI18n } from 'vue-i18n'; import { createI18n } from 'vue-i18n';
import { setHtmlLang } from './utils'
import lang from './locates' import lang from './locates'
@ -32,6 +33,9 @@ if (userLang) {
console.log("Selected language:", language); console.log("Selected language:", language);
// 初始设置HTML lang
setHtmlLang(language);
const i18n = createI18n<[MessageSchema], 'en' | 'zh_CN' | 'zh_TW' | 'ja' | 'ko'>({ const i18n = createI18n<[MessageSchema], 'en' | 'zh_CN' | 'zh_TW' | 'ja' | 'ko'>({
locale: language, locale: language,
fallbackLocale: 'en', fallbackLocale: 'en',

View File

@ -6,6 +6,21 @@
* @author feie9454 * @author feie9454
*/ */
/**
* HTML文档的lang属性
* @param locale - i18n的locale值 ( 'zh_CN', 'en', 'ja' )
*/
export function setHtmlLang(locale: string): void {
const langMap: Record<string, string> = {
'zh_CN': 'zh-CN',
'zh_TW': 'zh-TW',
'ja': 'ja',
'ko': 'ko',
'en': 'en'
};
document.documentElement.lang = langMap[locale] || 'en';
}
/** /**
* Darkens a hex color by a given percentage * Darkens a hex color by a given percentage
* @param hex - Hex color code (e.g. '#00D78D' or '00D78D') * @param hex - Hex color code (e.g. '#00D78D' or '00D78D')

View File

@ -149,15 +149,15 @@ function nextQuestion(i: number) {
.title { .title {
position: absolute; position: absolute;
left: 10%; left: 10%;
top: 32%; top: 30%;
width: 80%; width: 80%;
&:lang(en) { &:lang(en),&:lang(ja),&:lang(ko) {
top: 25%; top: 25%;
.q-title { .q-title {
width: 95%; width: 95%;
font-size: 1.75rem; font-size: 1.5rem;
} }
} }

View File

@ -153,7 +153,7 @@ onUnmounted(() => {
@include abs-pos(85%, 4.2%); @include abs-pos(85%, 4.2%);
position: relative; position: relative;
cursor: pointer; cursor: pointer;
width:20%; width:23%;
z-index: 1000; z-index: 1000;
} }
@ -161,8 +161,8 @@ onUnmounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 8px; gap: 4px;
padding: 8px 12px; padding: 8px 8px;
background: transparent; background: transparent;
border-radius: 20px; border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.6); border: 1px solid rgba(255, 255, 255, 0.6);

View File

@ -121,6 +121,12 @@ function submit() {
&:lang(en) { &:lang(en) {
font-size: 1.9rem; font-size: 1.9rem;
} }
&:lang(ja) {
font-size: 1.5rem;
}
&:lang(ko) {
font-size: 1.5rem;
}
} }
.inputs { .inputs {

View File

@ -51,12 +51,12 @@ const topFiveTraits = computed(() => {
return sortedTraits.slice(0, 5) as [[TraitName, number], ...[TraitName, number][]] return sortedTraits.slice(0, 5) as [[TraitName, number], ...[TraitName, number][]]
}) })
function jumpZh() { /* function jumpZh() {
window.location.href = 'weixin://dl/business/?appid=wxe6d33a608a6fd9a5&path=pages/brandstorm/index&query=promotionId=9' window.location.href = 'weixin://dl/business/?appid=wxe6d33a608a6fd9a5&path=pages/brandstorm/index&query=promotionId=9'
} }
function jumpEn() { function jumpEn() {
window.location.href = 'https://brandstorm.loreal.com' window.location.href = 'https://brandstorm.loreal.com'
} } */
</script> </script>
<template> <template>
@ -77,7 +77,7 @@ function jumpEn() {
</div> </div>
<div class="left-title"> <div class="left-title">
<img :src="imgs.icons[topDimension]" alt=""> <img :src="imgs.icons[topDimension]" alt="">
<span v-if="$i18n.locale === 'zh'" class="zh-title" <span v-if="$i18n.locale !== 'zh'" class="zh-title"
v-for="c in lang.zh_CN.questionData.DimensionName[topDimension].split('')"> v-for="c in lang.zh_CN.questionData.DimensionName[topDimension].split('')">
{{ c }}</span> {{ c }}</span>
<span v-if="$i18n.locale === 'en'" class="en-title"> <span v-if="$i18n.locale === 'en'" class="en-title">
@ -100,8 +100,6 @@ function jumpEn() {
:border-color="DimensionColors[topDimension]" :border-color="DimensionColors[topDimension]"
:rarity="rarity" /> :rarity="rarity" />
<div class="imgs"> <div class="imgs">
<img class="left" :src="t('report.bottomLeft')" alt=""
@click="$i18n.locale == 'zh' ? jumpZh() : jumpEn()">
<div class="right" @click="$emit('next')"> <div class="right" @click="$emit('next')">
<img :src="t('report.bottomRight')" alt="" class="botton" /> <img :src="t('report.bottomRight')" alt="" class="botton" />
@ -176,13 +174,21 @@ function jumpEn() {
text-indent: 2em; text-indent: 2em;
text-wrap: pretty; text-wrap: pretty;
&:lang(zh) { &[lang="zh_CN"], &[lang="zh_TW"] {
@include font(.80rem, 1.45, pre-wrap); @include font(.80rem, 1.45, pre-wrap);
} }
&:lang(en) { &[lang="en"] {
@include font(.80rem, 1.43, pre-wrap); @include font(.80rem, 1.43, pre-wrap);
} }
&[lang="ko"] {
@include font(.75rem, 1.43, pre-wrap);
}
&[lang="ja"] {
@include font(.75rem, 1.43, pre-wrap);
}
} }
} }

View File

@ -175,13 +175,21 @@ function jumpEn() {
text-indent: 2em; text-indent: 2em;
text-wrap: pretty; text-wrap: pretty;
&:lang(zh) { &[lang="zh_CN"], &[lang="zh_TW"] {
@include font(.80rem, 1.45, pre-wrap); @include font(.80rem, 1.45, pre-wrap);
} }
&:lang(en) { &[lang="en"] {
@include font(.80rem, 1.43, pre-wrap); @include font(.80rem, 1.43, pre-wrap);
} }
&[lang="ko"] {
@include font(.75rem, 1.43, pre-wrap);
}
&[lang="ja"] {
@include font(.75rem, 1.43, pre-wrap);
}
} }
} }

View File

@ -117,7 +117,8 @@ const lightProps = [{
<LightGrow class="bg" :shapes="lightProps" :animation-duration="4" <LightGrow class="bg" :shapes="lightProps" :animation-duration="4"
:blur-amount="60" background="#000" /> :blur-amount="60" background="#000" />
<div class="top"> <div class="top">
<div class="title-container" :style="{ opacity: opacityDuration }" :lang="$i18n.locale"> <div class="title-container" :style="{ opacity: opacityDuration }"
:lang="$i18n.locale">
<span class="title" v-html="t('result.title')"></span> <span class="title" v-html="t('result.title')"></span>
<span class="subtitle">{{ t('result.subtitle') }}</span> <span class="subtitle">{{ t('result.subtitle') }}</span>
</div> </div>
@ -125,10 +126,11 @@ const lightProps = [{
:title="`THE 5 L'Oréal DIMENSIONS`" :score-radio="1" :title="`THE 5 L'Oréal DIMENSIONS`" :score-radio="1"
:subtitle="'OF POTENTIAL'" /> :subtitle="'OF POTENTIAL'" />
<Tags class="tags" :top-five-traits="topFiveTraits" :username="t('you')" <Tags class="tags" :top-five-traits="topFiveTraits"
border-color="#ffffff" :style="{ opacity: opacityDuration }" :username="t('you')" border-color="#ffffff"
:rarity="rarity" /> :style="{ opacity: opacityDuration }" :rarity="rarity" />
<div class="slide-down" :style="{ opacity: opacityDuration }"> <div class="slide-down" :style="{ opacity: opacityDuration }"
:lang="$i18n.locale">
<span v-html="t('result.slideDown')"></span> <span v-html="t('result.slideDown')"></span>
<img :src="imgs.slideDown" alt=""> <img :src="imgs.slideDown" alt="">
</div> </div>
@ -186,11 +188,13 @@ const lightProps = [{
justify-content: space-around; justify-content: space-around;
align-items: center; align-items: center;
gap: 1em; gap: 1em;
&:lang(en) { &:lang(en) {
button { button {
font-size: .85em; font-size: .85em;
} }
} }
button { button {
text-transform: uppercase; text-transform: uppercase;
flex: 1; flex: 1;
@ -241,13 +245,20 @@ const lightProps = [{
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
gap: .5em; gap: .5em;
&:lang(en) { &:lang(en) {
font-size: .85rem; font-size: .85rem;
} }
&:lang(ja) {
font-size: .75rem;
}
.title { .title {
@include font(2.2em, 1, pre-wrap, center); @include font(2.2em, 1, pre-wrap, center);
text-transform: uppercase; text-transform: uppercase;
width: max-content; width: max-content;
max-width: 80vw;
} }
.subtitle { .subtitle {
@ -273,9 +284,16 @@ const lightProps = [{
gap: 1.25em; gap: 1.25em;
width: max-content; width: max-content;
text-transform: uppercase; text-transform: uppercase;
img { img {
width: 4em; width: 4em;
animation: slide-down 1.5s ease-in-out infinite; animation: slide-down 1.5s ease-in-out infinite;
} }
&:lang(ja),
&:lang(ko) {
font-size: 0.7em;
}
} }
</style> </style>