mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-10-01 07:37:55 -05:00
Placements suffix refactor, fix English shown when JP language selected
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ordinalSuffix } from "~/utils/i18n";
|
||||
import {
|
||||
FIRST_PLACEMENT_ICON_PATH,
|
||||
SECOND_PLACEMENT_ICON_PATH,
|
||||
@@ -38,18 +39,11 @@ export function Placement({
|
||||
showAsSuperscript = true,
|
||||
plain = false,
|
||||
}: PlacementProps) {
|
||||
const { t } = useTranslation(undefined, {});
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const ordinalSuffix = t("results.placeSuffix", {
|
||||
count: placement,
|
||||
ordinal: true,
|
||||
// no suffix is a better default than english
|
||||
defaultValue: "",
|
||||
fallbackLng: [],
|
||||
});
|
||||
|
||||
const isSuperscript = showAsSuperscript && ordinalSuffix.startsWith("^");
|
||||
const ordinalSuffixText = ordinalSuffix.replace(/^\^/, "");
|
||||
const suffix = ordinalSuffix(placement, i18n.language);
|
||||
const isSuperscript = showAsSuperscript && suffix.startsWith("^");
|
||||
const ordinalSuffixText = suffix.replace(/^\^/, "");
|
||||
|
||||
const iconPath = textOnly ? null : getSpecialPlacementIconPath(placement);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { LanguageCode } from "~/modules/i18n/config";
|
||||
import type { Namespace } from "~/modules/i18n/resources.server";
|
||||
import { logger } from "./logger";
|
||||
import { assertType } from "./types";
|
||||
@@ -76,3 +77,56 @@ export function countryCodeToTranslatedName({
|
||||
return countryCode; // fallback to the code itself if display name fails
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ordinal placement suffixes per language, keyed by the CLDR ordinal plural
|
||||
* category given by `Intl.PluralRules(language, { type: "ordinal" })`. A leading
|
||||
* `^` marks a suffix that should render as superscript. Languages without a
|
||||
* written ordinal suffix use explicit `null` – rendering no suffix is preferred
|
||||
* over borrowing the English one.
|
||||
*/
|
||||
const ORDINAL_SUFFIXES: Record<
|
||||
LanguageCode,
|
||||
Partial<Record<Intl.LDMLPluralRule, string>> | null
|
||||
> = {
|
||||
da: null,
|
||||
he: null,
|
||||
nl: null,
|
||||
en: { one: "^st", two: "^nd", few: "^rd", other: "^th" },
|
||||
"es-ES": { other: "º" },
|
||||
"es-US": { other: "^o" },
|
||||
"fr-CA": { one: "^er", other: "^ème" },
|
||||
"fr-EU": { one: "^er", other: "^ème" },
|
||||
de: { other: "^." },
|
||||
it: { many: "^o", other: "^o" },
|
||||
ja: { other: "位" },
|
||||
ko: { other: "^등" },
|
||||
pl: { other: "^." },
|
||||
"pt-BR": { other: "^º" },
|
||||
ru: { other: "^ое" },
|
||||
zh: { other: "名" },
|
||||
};
|
||||
|
||||
const pluralRulesCache = new Map<string, Intl.PluralRules>();
|
||||
|
||||
/**
|
||||
* Returns the localized ordinal suffix for a placement number in the given
|
||||
* language (e.g. `"^st"` for `1` in English). A leading `^` marks a suffix the
|
||||
* caller should render as superscript. Returns an empty string for languages
|
||||
* without a written ordinal suffix.
|
||||
*/
|
||||
export function ordinalSuffix(placement: number, language: string): string {
|
||||
const category = ordinalPluralRules(language).select(placement);
|
||||
|
||||
return ORDINAL_SUFFIXES[language as LanguageCode]?.[category] ?? "";
|
||||
}
|
||||
|
||||
function ordinalPluralRules(language: string): Intl.PluralRules {
|
||||
let rules = pluralRulesCache.get(language);
|
||||
if (!rules) {
|
||||
rules = new Intl.PluralRules(language, { type: "ordinal" });
|
||||
pluralRulesCache.set(language, rules);
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
@@ -174,10 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "",
|
||||
"maps.validation.VALID": "Banepuljen er gyldig",
|
||||
"results": "Resultater",
|
||||
"results.placeSuffix_one": "",
|
||||
"results.placeSuffix_other": "",
|
||||
"results.placeSuffix_two": "",
|
||||
"results.placeSuffix_few": "",
|
||||
"forms.name": "Navn",
|
||||
"forms.description": "Beskrivelse",
|
||||
"forms.errors.title": "Følgende fejl skal rettes",
|
||||
|
||||
@@ -174,10 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "",
|
||||
"maps.validation.VALID": "Arenen-Pool ok!",
|
||||
"results": "Ergebnisse",
|
||||
"results.placeSuffix_one": "^.",
|
||||
"results.placeSuffix_other": "^.",
|
||||
"results.placeSuffix_two": "^.",
|
||||
"results.placeSuffix_few": "^.",
|
||||
"forms.name": "Name",
|
||||
"forms.description": "Beschreibung",
|
||||
"forms.errors.title": "Diese Fehler müssen behoben werden",
|
||||
|
||||
@@ -174,10 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "Can't pick the tiebreaker stage",
|
||||
"maps.validation.VALID": "Map pool ok!",
|
||||
"results": "Results",
|
||||
"results.placeSuffix_one": "^st",
|
||||
"results.placeSuffix_two": "^nd",
|
||||
"results.placeSuffix_few": "^rd",
|
||||
"results.placeSuffix_other": "^th",
|
||||
"forms.name": "Name",
|
||||
"forms.description": "Description",
|
||||
"forms.errors.title": "Following errors need to be fixed",
|
||||
|
||||
@@ -174,11 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "No se permite el escenario de desempate",
|
||||
"maps.validation.VALID": "¡Rotación de mapas OK!",
|
||||
"results": "Resultados",
|
||||
"results.placeSuffix_one": "º",
|
||||
"results.placeSuffix_many": "º",
|
||||
"results.placeSuffix_other": "º",
|
||||
"results.placeSuffix_two": "º",
|
||||
"results.placeSuffix_few": "º",
|
||||
"forms.name": "Nombre",
|
||||
"forms.description": "Descripción",
|
||||
"forms.errors.title": "Deben corregirse los siguientes errores:",
|
||||
|
||||
@@ -174,11 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "No se permite el escenario para desempate",
|
||||
"maps.validation.VALID": "¡Grupo de escenarios OK!",
|
||||
"results": "Resultados",
|
||||
"results.placeSuffix_one": "^o",
|
||||
"results.placeSuffix_many": "",
|
||||
"results.placeSuffix_other": "^o",
|
||||
"results.placeSuffix_two": "^o",
|
||||
"results.placeSuffix_few": "^o",
|
||||
"forms.name": "Nombre",
|
||||
"forms.description": "Descripción",
|
||||
"forms.errors.title": "Los siguientes errores deben corregirse",
|
||||
|
||||
@@ -174,11 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "",
|
||||
"maps.validation.VALID": "Liste de stages valide !",
|
||||
"results": "Résultats",
|
||||
"results.placeSuffix_one": "^er",
|
||||
"results.placeSuffix_many": "",
|
||||
"results.placeSuffix_other": "^ème",
|
||||
"results.placeSuffix_two": "^ème",
|
||||
"results.placeSuffix_few": "^ème",
|
||||
"forms.name": "Nom",
|
||||
"forms.description": "Description",
|
||||
"forms.errors.title": "Les erreurs ci-dessous doivent être réglées",
|
||||
|
||||
@@ -174,11 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "Vous ne pouvez pas choisir le stage décisif",
|
||||
"maps.validation.VALID": "Liste de stages valide !",
|
||||
"results": "Résultats",
|
||||
"results.placeSuffix_one": "^er",
|
||||
"results.placeSuffix_many": "",
|
||||
"results.placeSuffix_other": "^ème",
|
||||
"results.placeSuffix_two": "^ème",
|
||||
"results.placeSuffix_few": "^ème",
|
||||
"forms.name": "Nom",
|
||||
"forms.description": "Description",
|
||||
"forms.errors.title": "Les erreurs ci-dessous doivent être réglées",
|
||||
|
||||
@@ -174,10 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "",
|
||||
"maps.validation.VALID": "מאגר המפות בסדר!",
|
||||
"results": "תוצאות",
|
||||
"results.placeSuffix_one": "^st",
|
||||
"results.placeSuffix_two": "^nd",
|
||||
"results.placeSuffix_other": "^th",
|
||||
"results.placeSuffix_few": "^rd",
|
||||
"forms.name": "שם",
|
||||
"forms.description": "תיאור",
|
||||
"forms.errors.title": "יש לתקן את השגיאות הבאות",
|
||||
|
||||
@@ -174,11 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "Non può venir scelta la mappa per lo spareggio",
|
||||
"maps.validation.VALID": "Pool scenari OK!",
|
||||
"results": "Risultati",
|
||||
"results.placeSuffix_one": "^o",
|
||||
"results.placeSuffix_many": "",
|
||||
"results.placeSuffix_other": "^o",
|
||||
"results.placeSuffix_two": "^o",
|
||||
"results.placeSuffix_few": "^o",
|
||||
"forms.name": "Nome",
|
||||
"forms.description": "Descrizione",
|
||||
"forms.errors.title": "I seguenti errori devono essere corretti",
|
||||
|
||||
@@ -174,8 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "タイブレーカ(引き分けの場合)のステージを決めることができません。",
|
||||
"maps.validation.VALID": "登録できるマップリストです!",
|
||||
"results": "結果",
|
||||
"results.placeSuffix_two": "位",
|
||||
"results.placeSuffix_few": "位",
|
||||
"forms.name": "名前",
|
||||
"forms.description": "説明",
|
||||
"forms.errors.title": "以下のエラーの修正が必要です",
|
||||
|
||||
@@ -174,8 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "",
|
||||
"maps.validation.VALID": "Map pool ok!",
|
||||
"results": "결과",
|
||||
"results.placeSuffix_two": "^등",
|
||||
"results.placeSuffix_few": "^등",
|
||||
"forms.name": "이름",
|
||||
"forms.description": "설명",
|
||||
"forms.errors.title": "다음 문제들을 해결해야 합니다",
|
||||
|
||||
@@ -174,10 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "",
|
||||
"maps.validation.VALID": "",
|
||||
"results": "Uitslagen",
|
||||
"results.placeSuffix_one": "",
|
||||
"results.placeSuffix_other": "",
|
||||
"results.placeSuffix_two": "",
|
||||
"results.placeSuffix_few": "",
|
||||
"forms.name": "Naam",
|
||||
"forms.description": "Beschrijving",
|
||||
"forms.errors.title": "De volgende errors moeten nog worden opgelost",
|
||||
|
||||
@@ -174,11 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "",
|
||||
"maps.validation.VALID": "Pula map jest okej!",
|
||||
"results": "Wyniki",
|
||||
"results.placeSuffix_one": "^.",
|
||||
"results.placeSuffix_few": "^.",
|
||||
"results.placeSuffix_many": "",
|
||||
"results.placeSuffix_other": "^.",
|
||||
"results.placeSuffix_two": "^.",
|
||||
"forms.name": "Imię",
|
||||
"forms.description": "Opis",
|
||||
"forms.errors.title": "Dane błędy muszą zostać naprawione",
|
||||
|
||||
@@ -174,11 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "Não é possível escolher o mapa de desempate",
|
||||
"maps.validation.VALID": "Seleção de mapas OK!",
|
||||
"results": "Resultados",
|
||||
"results.placeSuffix_one": "^º",
|
||||
"results.placeSuffix_many": "",
|
||||
"results.placeSuffix_other": "^º",
|
||||
"results.placeSuffix_two": "^º",
|
||||
"results.placeSuffix_few": "^º",
|
||||
"forms.name": "Nome",
|
||||
"forms.description": "Descrição",
|
||||
"forms.errors.title": "Os seguintes erros precisam ser corrigidos",
|
||||
|
||||
@@ -174,11 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "Нельзя выбрать арену-тайбрейкер",
|
||||
"maps.validation.VALID": "Список карт в порядке!",
|
||||
"results": "Результаты",
|
||||
"results.placeSuffix_one": "^ое",
|
||||
"results.placeSuffix_few": "^ье",
|
||||
"results.placeSuffix_many": "",
|
||||
"results.placeSuffix_other": "^ое",
|
||||
"results.placeSuffix_two": "^ое",
|
||||
"forms.name": "Название",
|
||||
"forms.description": "Описание",
|
||||
"forms.errors.title": "Указанные ошибки должны быть исправлены",
|
||||
|
||||
@@ -174,8 +174,6 @@
|
||||
"maps.validation.INCLUDES_TIEBREAKER": "无法选择决胜局地图",
|
||||
"maps.validation.VALID": "地图池已完成!",
|
||||
"results": "成绩",
|
||||
"results.placeSuffix_two": "^nd",
|
||||
"results.placeSuffix_few": "^rd",
|
||||
"forms.name": "名称",
|
||||
"forms.description": "简介",
|
||||
"forms.errors.title": "以下错误需要修正",
|
||||
|
||||
Reference in New Issue
Block a user