[Localization] Use proper postpositional particle for Korean (#1759)

In Korean, postpositional particles vary depending on whether the
preceding syllable ends in a consonant or a vowel. Currently there
is no ability differentiate between the two types of particles, so both
forms are being used at the same time.
To remedy this problem, I added the relevant i18next processor to
properly select and print the correct form of the particle.
This commit is contained in:
Yurical 2024-06-04 10:38:52 +09:00 committed by GitHub
parent 69da96d543
commit e0fd11746f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 47 additions and 35 deletions

9
package-lock.json generated
View File

@ -12,6 +12,7 @@
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"i18next": "^23.11.1", "i18next": "^23.11.1",
"i18next-browser-languagedetector": "^7.2.1", "i18next-browser-languagedetector": "^7.2.1",
"i18next-korean-postposition-processor": "^1.0.0",
"json-stable-stringify": "^1.1.0", "json-stable-stringify": "^1.1.0",
"phaser": "^3.70.0", "phaser": "^3.70.0",
"phaser3-rex-plugins": "^1.1.84" "phaser3-rex-plugins": "^1.1.84"
@ -3615,6 +3616,14 @@
"cross-fetch": "4.0.0" "cross-fetch": "4.0.0"
} }
}, },
"node_modules/i18next-korean-postposition-processor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/i18next-korean-postposition-processor/-/i18next-korean-postposition-processor-1.0.0.tgz",
"integrity": "sha512-ruNXjI9awsFK6Ie+F9gYaMW8ciLMuCkeRjH9QkSv2Wb8xI0mnm773v3M9eua8dtvAXudIUk4p6Ho7hNkEASXDg==",
"peerDependencies": {
"i18next": ">=8.4.0"
}
},
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",

View File

@ -43,6 +43,7 @@
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"i18next": "^23.11.1", "i18next": "^23.11.1",
"i18next-browser-languagedetector": "^7.2.1", "i18next-browser-languagedetector": "^7.2.1",
"i18next-korean-postposition-processor": "^1.0.0",
"json-stable-stringify": "^1.1.0", "json-stable-stringify": "^1.1.0",
"phaser": "^3.70.0", "phaser": "^3.70.0",
"phaser3-rex-plugins": "^1.1.84" "phaser3-rex-plugins": "^1.1.84"

View File

@ -1,6 +1,6 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n"; import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const abilityTriggers: SimpleTranslationEntries = { export const abilityTriggers: SimpleTranslationEntries = {
"blockRecoilDamage" : "{{pokemonName}}(는)은 {{abilityName}} 때문에\n반동 데미지를 받지 않는다!", "blockRecoilDamage" : "{{pokemonName}}[[는]] {{abilityName}} 때문에\n반동 데미지를 받지 않는다!",
"badDreams": "{{pokemonName}}(는)은\n나이트메어 때문에 시달리고 있다!", "badDreams": "{{pokemonName}}[[는]]\n나이트메어 때문에 시달리고 있다!",
} as const; } as const;

View File

@ -1,21 +1,21 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n"; import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const battle: SimpleTranslationEntries = { export const battle: SimpleTranslationEntries = {
"bossAppeared": "{{bossName}}(이)가 나타났다.", "bossAppeared": "{{bossName}}[[가]] 나타났다.",
"trainerAppeared": "{{trainerName}}(이)가\n승부를 걸어왔다!", "trainerAppeared": "{{trainerName}}[[가]]\n승부를 걸어왔다!",
"trainerAppearedDouble": "{{trainerName}}(이)가\n승부를 걸어왔다!", "trainerAppearedDouble": "{{trainerName}}[[가]]\n승부를 걸어왔다!",
"trainerSendOut": "{{trainerName}}(는)은\n{{pokemonName}}(를)을 내보냈다!", "trainerSendOut": "{{trainerName}}[[는]]\n{{pokemonName}}[[를]] 내보냈다!",
"singleWildAppeared": "앗! 야생 {{pokemonName}}(이)가\n튀어나왔다!", "singleWildAppeared": "앗! 야생 {{pokemonName}}[[가]]\n튀어나왔다!",
"multiWildAppeared": "야생 {{pokemonName1}}(과)와\n{{pokemonName2}}(이)가 튀어나왔다!", "multiWildAppeared": "야생 {{pokemonName1}}[[와]]\n{{pokemonName2}}[[가]] 튀어나왔다!",
"playerComeBack": "돌아와, {{pokemonName}}!", "playerComeBack": "돌아와, {{pokemonName}}!",
"trainerComeBack": "{{trainerName}}(는)은 {{pokemonName}}를(을) 넣어버렸다!", "trainerComeBack": "{{trainerName}}[[는]] {{pokemonName}}[[를]] 넣어버렸다!",
"playerGo": "가랏! {{pokemonName}}!", "playerGo": "가랏! {{pokemonName}}!",
"trainerGo": "{{trainerName}}(는)은 {{pokemonName}}를(을) 내보냈다!", "trainerGo": "{{trainerName}}[[는]] {{pokemonName}}[[를]] 내보냈다!",
"switchQuestion": "{{pokemonName}}를(을)\n교체하시겠습니까?", "switchQuestion": "{{pokemonName}}[[를]]\n교체하시겠습니까?",
"trainerDefeated": "{{trainerName}}과(와)의\n승부에서 이겼다!", "trainerDefeated": "{{trainerName}}[[와]]의\n승부에서 이겼다!",
"moneyWon": "상금으로\n₽{{moneyAmount}}을 손에 넣었다!", "moneyWon": "상금으로\n₽{{moneyAmount}}을 손에 넣었다!",
"pokemonCaught": "신난다-!\n{{pokemonName}}(를)을 잡았다!", "pokemonCaught": "신난다-!\n{{pokemonName}}[[를]] 잡았다!",
"partyFull": "지닌 포켓몬이 가득 찼습니다. {{pokemonName}}를(을)\n대신해 포켓몬을 놓아주시겠습니까?", "partyFull": "지닌 포켓몬이 가득 찼습니다. {{pokemonName}}[[를]]\n대신해 포켓몬을 놓아주시겠습니까?",
"pokemon": "포켓몬", "pokemon": "포켓몬",
"sendOutPokemon": "가랏! {{pokemonName}}!", "sendOutPokemon": "가랏! {{pokemonName}}!",
"hitResultCriticalHit": "급소에 맞았다!", "hitResultCriticalHit": "급소에 맞았다!",
@ -25,22 +25,22 @@ export const battle: SimpleTranslationEntries = {
"hitResultOneHitKO": "일격필살!", "hitResultOneHitKO": "일격필살!",
"attackFailed": "하지만 실패했다!", "attackFailed": "하지만 실패했다!",
"attackHitsCount": "{{count}}번 맞았다!", "attackHitsCount": "{{count}}번 맞았다!",
"expGain": "{{pokemonName}}(는)은\n{{exp}} 경험치를 얻었다!", "expGain": "{{pokemonName}}[[는]]\n{{exp}} 경험치를 얻었다!",
"levelUp": "{{pokemonName}}(는)은\n레벨 {{level}}(으)로 올랐다!", "levelUp": "{{pokemonName}}[[는]]\n레벨 {{level}}[[로]] 올랐다!",
"learnMove": "{{pokemonName}}(는)은 새로\n{{moveName}}를(을) 배웠다!", "learnMove": "{{pokemonName}}[[는]] 새로\n{{moveName}}[[를]] 배웠다!",
"learnMovePrompt": "{{pokemonName}}(는)은 새로\n{{moveName}}를(을) 배우고 싶다!…", "learnMovePrompt": "{{pokemonName}}[[는]] 새로\n{{moveName}}[[를]] 배우고 싶다!…",
"learnMoveLimitReached": "그러나 {{pokemonName}}(는)은 기술을 4개\n알고 있으므로 더 이상 배울 수 없다!", "learnMoveLimitReached": "그러나 {{pokemonName}}[[는]] 기술을 4개\n알고 있으므로 더 이상 배울 수 없다!",
"learnMoveReplaceQuestion": "{{moveName}} 대신 다른 기술을 잊게 하겠습니까?", "learnMoveReplaceQuestion": "{{moveName}} 대신 다른 기술을 잊게 하겠습니까?",
"learnMoveStopTeaching": "그럼… {{moveName}}를(을)\n배우는 것을 포기하겠습니까?", "learnMoveStopTeaching": "그럼… {{moveName}}[[를]]\n배우는 것을 포기하겠습니까?",
"learnMoveNotLearned": "{{pokemonName}}(는)은 {{moveName}}를(을)\n결국 배우지 않았다!", "learnMoveNotLearned": "{{pokemonName}}[[는]] {{moveName}}[[를]]\n결국 배우지 않았다!",
"learnMoveForgetQuestion": "어느 기술을 잊게 하고싶은가?", "learnMoveForgetQuestion": "어느 기술을 잊게 하고싶은가?",
"learnMoveForgetSuccess": "{{pokemonName}}(는)은 {{moveName}}를(을) 깨끗이 잊었다!", "learnMoveForgetSuccess": "{{pokemonName}}[[는]] {{moveName}}[[를]] 깨끗이 잊었다!",
"countdownPoof": "@d{32}1, @d{15}2, @d{15}… @d{15}… @d{30}@s{pb_bounce_1}짠!", "countdownPoof": "@d{32}1, @d{15}2, @d{15}… @d{15}… @d{30}@s{pb_bounce_1}짠!",
"learnMoveAnd": "그리고…", "learnMoveAnd": "그리고…",
"levelCapUp": "레벨의 최대치가\n{{levelCap}}까지 상승했다!", "levelCapUp": "레벨의 최대치가\n{{levelCap}}까지 상승했다!",
"moveNotImplemented": "{{moveName}}(는)은 아직 구현되지 않아 사용할 수 없다…", "moveNotImplemented": "{{moveName}}[[는]] 아직 구현되지 않아 사용할 수 없다…",
"moveNoPP": "기술의 남은 포인트가 없다!", "moveNoPP": "기술의 남은 포인트가 없다!",
"moveDisabled": "{{moveName}}를(을) 쓸 수 없다!", "moveDisabled": "{{moveName}}[[를]] 쓸 수 없다!",
"noPokeballForce": "본 적 없는 힘이\n볼을 사용하지 못하게 한다.", "noPokeballForce": "본 적 없는 힘이\n볼을 사용하지 못하게 한다.",
"noPokeballTrainer": "다른 트레이너의 포켓몬은 잡을 수 없다!", "noPokeballTrainer": "다른 트레이너의 포켓몬은 잡을 수 없다!",
"noPokeballMulti": "안돼! 2마리 있어서\n목표를 정할 수가 없어…!", "noPokeballMulti": "안돼! 2마리 있어서\n목표를 정할 수가 없어…!",

View File

@ -5,5 +5,5 @@ export const commandUiHandler: SimpleTranslationEntries = {
"ball": "볼", "ball": "볼",
"pokemon": "포켓몬", "pokemon": "포켓몬",
"run": "도망간다", "run": "도망간다",
"actionMessage": "{{pokemonName}}(는)은 무엇을 할까?", "actionMessage": "{{pokemonName}}[[는]] 무엇을 할까?",
} as const; } as const;

View File

@ -38,9 +38,9 @@ export const menu: SimpleTranslationEntries = {
"girl": "여자", "girl": "여자",
"evolving": "…오잉!?\n{{pokemonName}}의 모습이…!", "evolving": "…오잉!?\n{{pokemonName}}의 모습이…!",
"stoppedEvolving": "얼라리…?\n{{pokemonName}}의 변화가 멈췄다!", "stoppedEvolving": "얼라리…?\n{{pokemonName}}의 변화가 멈췄다!",
"pauseEvolutionsQuestion": "{{pokemonName}}를(을) 진화하지 않게 만드시겠습니까?\n포켓몬 화면에서 다시 활성화시킬 수 있습니다.", "pauseEvolutionsQuestion": "{{pokemonName}}[[를]] 진화하지 않게 만드시겠습니까?\n포켓몬 화면에서 다시 활성화시킬 수 있습니다.",
"evolutionsPaused": "{{pokemonName}}의 진화가 비활성화되었다.", "evolutionsPaused": "{{pokemonName}}의 진화가 비활성화되었다.",
"evolutionDone": "축하합니다! {{pokemonName}}(는)은\n{{evolvedPokemonName}}(으)로 진화했습니다!", "evolutionDone": "축하합니다! {{pokemonName}}[[는]]\n{{evolvedPokemonName}}[[로]] 진화했습니다!",
"dailyRankings": "일간 랭킹", "dailyRankings": "일간 랭킹",
"weeklyRankings": "주간 랭킹", "weeklyRankings": "주간 랭킹",
"noRankings": "랭킹 정보 없음", "noRankings": "랭킹 정보 없음",

View File

@ -12,8 +12,8 @@ export const modifierType: ModifierTypeTranslationEntries = {
}, },
"PokemonHeldItemModifierType": { "PokemonHeldItemModifierType": {
extra: { extra: {
"inoperable": "{{pokemonName}}(는)은\n이 아이템을 얻을 수 없다!", "inoperable": "{{pokemonName}}[[는]]\n이 아이템을 얻을 수 없다!",
"tooMany": "{{pokemonName}}(는)은\n이 아이템을 너무 많이 갖고 있다!", "tooMany": "{{pokemonName}}[[는]]\n이 아이템을 너무 많이 갖고 있다!",
} }
}, },
"PokemonHpRestoreModifierType": { "PokemonHpRestoreModifierType": {
@ -46,13 +46,13 @@ export const modifierType: ModifierTypeTranslationEntries = {
}, },
"PokemonNatureChangeModifierType": { "PokemonNatureChangeModifierType": {
name: "{{natureName}}민트", name: "{{natureName}}민트",
description: "포켓몬의 성격을 {{natureName}}(으)로 바꾸고 스타팅에도 등록한다.", description: "포켓몬의 성격을 {{natureName}}[[로]] 바꾸고 스타팅에도 등록한다.",
}, },
"DoubleBattleChanceBoosterModifierType": { "DoubleBattleChanceBoosterModifierType": {
description: "{{battleCount}}번의 배틀 동안 더블 배틀이 등장할 확률 두 배", description: "{{battleCount}}번의 배틀 동안 더블 배틀이 등장할 확률 두 배",
}, },
"TempBattleStatBoosterModifierType": { "TempBattleStatBoosterModifierType": {
description: "자신의 모든 포켓몬이 5번의 배틀 동안 {{tempBattleStatName}}(이)가 한 단계 증가" description: "자신의 모든 포켓몬이 5번의 배틀 동안 {{tempBattleStatName}}[[가]] 한 단계 증가"
}, },
"AttackTypeBoosterModifierType": { "AttackTypeBoosterModifierType": {
description: "지니게 하면 {{moveType}}타입 기술의 위력이 20% 상승", description: "지니게 하면 {{moveType}}타입 기술의 위력이 20% 상승",
@ -97,7 +97,7 @@ export const modifierType: ModifierTypeTranslationEntries = {
}, },
"TmModifierType": { "TmModifierType": {
name: "No.{{moveId}} {{moveName}}", name: "No.{{moveId}} {{moveName}}",
description: "포켓몬에게 {{moveName}}를(을) 가르침", description: "포켓몬에게 {{moveName}}[[를]] 가르침",
}, },
"EvolutionItemModifierType": { "EvolutionItemModifierType": {
description: "어느 특정 포켓몬을 진화", description: "어느 특정 포켓몬을 진화",

View File

@ -15,12 +15,12 @@ export const weather: SimpleTranslationEntries = {
"sandstormStartMessage": "모래바람이 불기 시작했다!", "sandstormStartMessage": "모래바람이 불기 시작했다!",
"sandstormLapseMessage": "모래바람이 세차게 분다", "sandstormLapseMessage": "모래바람이 세차게 분다",
"sandstormClearMessage": "모래바람이 가라앉았다!", "sandstormClearMessage": "모래바람이 가라앉았다!",
"sandstormDamageMessage": "모래바람이\n{{pokemonPrefix}}{{pokemonName}}를(을) 덮쳤다!", "sandstormDamageMessage": "모래바람이\n{{pokemonPrefix}}{{pokemonName}}[[를]] 덮쳤다!",
"hailStartMessage": "싸라기눈이 내리기 시작했다!", "hailStartMessage": "싸라기눈이 내리기 시작했다!",
"hailLapseMessage": "싸라기눈이 계속 내리고 있다", "hailLapseMessage": "싸라기눈이 계속 내리고 있다",
"hailClearMessage": "싸라기눈이 그쳤다!", "hailClearMessage": "싸라기눈이 그쳤다!",
"hailDamageMessage": "싸라기눈이\n{{pokemonPrefix}}{{pokemonName}}를(을) 덮쳤다!", "hailDamageMessage": "싸라기눈이\n{{pokemonPrefix}}{{pokemonName}}[[를]] 덮쳤다!",
"snowStartMessage": "눈이 내리기 시작했다!", "snowStartMessage": "눈이 내리기 시작했다!",
"snowLapseMessage": "눈이 계속 내리고 있다", "snowLapseMessage": "눈이 계속 내리고 있다",

View File

@ -1,5 +1,6 @@
import i18next from "i18next"; import i18next from "i18next";
import LanguageDetector from "i18next-browser-languagedetector"; import LanguageDetector from "i18next-browser-languagedetector";
import processor, { KoreanPostpositionProcessor } from "i18next-korean-postposition-processor";
import { deConfig } from "#app/locales/de/config.js"; import { deConfig } from "#app/locales/de/config.js";
import { enConfig } from "#app/locales/en/config.js"; import { enConfig } from "#app/locales/en/config.js";
@ -148,7 +149,7 @@ export function initI18n(): void {
* A: In src/system/settings.ts, add a new case to the Setting.Language switch statement. * A: In src/system/settings.ts, add a new case to the Setting.Language switch statement.
*/ */
i18next.use(LanguageDetector).init({ i18next.use(LanguageDetector).use(processor).use(new KoreanPostpositionProcessor()).init({
lng: lang, lng: lang,
nonExplicitSupportedLngs: true, nonExplicitSupportedLngs: true,
fallbackLng: "en", fallbackLng: "en",
@ -186,6 +187,7 @@ export function initI18n(): void {
...koConfig ...koConfig
}, },
}, },
postProcess: ["korean-postposition"],
}); });
} }