mirror of
https://github.com/brues-code/ClassicAPI.git
synced 2026-11-10 20:07:37 +00:00
feat(localization): complete and correct the Russian locale (#130)
* feat(localization): complete and correct the Russian locale
The Russian locale was 99 keys behind enUS, so a Russian client kept the
English strings for ClassicAPI's own options: the AddOn Options title and
its game-menu button, every page name, header, control label and tooltip,
the AoE Loot binding, and the generic strings the options pages gained
(FOCUS_TOKEN_NOT_FOUND, LUA_ERROR, RELOADUI, PLAYER_LIST_DELIMITER,
SYMBOLIC_NEGATIVE_NUMBER, SYMBOLIC_POSITIVE_NUMBER and the money texture
formats). All 327 keys are now present, in enUS order, so the two files
diff key by key.
Focus follows the client's modern terms — «Взять в фокус», «Сбросить
фокус», «Выбрать фокус» and «Взять цель в фокус» — matching the existing
/фокус and /сбросфокуса aliases. The rest uses the 1.12 Russian client's
vocabulary where it has one (модификация, цель, панель команд, время
восстановления, табличка с именем) and «динамическое выделение» /
«мягкая цель» for action targeting. The money strings keep their |4
plural forms, which GetCoinText expects.
Fixes:
- LARGE_NUMBER_SEPERATOR was "", which turned digit grouping off. It is
now a space, as in the tooltip's 1 234 567.
- FIRST/SECOND_NUMBER_CAP and their _NO_SPACE twins carry the leading
space, because Abbreviate concatenates the number and the suffix
directly.
- EQUIPMENT_MANAGER_ITEMS_MISSING_TOOLTIP printed a slot number where
the missing count belongs; it now takes the missing count.
- SETTINGS_BIND_KEY_TO_COMMAND_OR_CANCEL was missing a space before
«или нажмите».
- SET_FRIENDNOTE_LABEL had a |3-5 declension token, which 1.12 renders
literally.
- ITEM_SLOTS_IGNORED and ITEMS_NOT_IN_INVENTORY had wrong third plural
forms, ITEM_MOD_HIT_MELEE_RATING_SHORT had a double space, and the flat
stat labels (Защита, Уклонение, Парирование, Блок) read wrong after a
signed value, so they use the same «к …» form as the other ITEM_MOD
labels.
The TOC gains ## Notes-ruRU next to ## Notes.
* fix(localization): restore retail ruRU focus, number and log strings
Follow-up to the Russian locale completion, which had changed several
strings that the retail ruRU client already translates. Each is now
the retail text, or a correction where the new wording was wrong:
- Focus: the FOCUSTARGET / TARGETFOCUS binding labels were swapped
(FOCUSTARGET runs FocusUnit("target"), TARGETFOCUS runs
TargetUnit("focus")). All focus strings use retail ruRU's terms
again: Запомнить цель, Восстановить цель, Отменить запоминание цели,
Запомненная цель, <фокус отсутствует>.
- EVENTTRACE_LOG_DISCARD is the message logged after the log is
discarded; it had become an option-style label. Retail: Журнал
сброшен. EVENTTRACE_BUTTON_DISCARD_FILTER read "cancel all"; retail:
Сбросить все.
- FIRST/SECOND_NUMBER_CAP and their _NO_SPACE variants go back to
retail Т / М. AbbreviateNumbers appends the _NO_SPACE suffix
directly by design ("1.2K"), so " тыс." widened compact numbers.
- LARGE_NUMBER_SEPERATOR goes back to retail "" (as frFR). A plain
space also let a number wrap across lines. The separate-large-numbers
tooltip drops its "1 234 567" example, which no longer applies.
- The loose-target option read "whom the soft target follows". The
normal target is the one that follows, so it is now "обычная цель",
and the choices name the soft enemy / soft friend.
* fix(localization): pick language rules by locale name, not 4.3.4's index
1.12.1's locale index (VAR_LOCALE_INDEX, 0xC0E080) is the Wow.ini
Language+Country tag's position in the eight-entry name table at
0x8558A4 (enUS koKR frFR deDE zhCN zhTW esES xxYY), stored once at
0x402562 and 0 when nothing matches. It is never 8. The Russian branches
ported from 4.3.4 switched on 4.3.4's own slot numbers (esMX 7, ruRU 8),
so they could never run: a client whose GetLocale() is "ruRU" fell
through to the default plural rule ("5 предмета") and got no ё/е folding
in bag search.
Localization::Locale::Client() reads the same entry Script_GetLocale
(0x48D8B0) pushes, table[index], and Is(code) compares against it. The
language-rule sites now go through it:
- Text::Grammar's plural index (|4 expansion) keys koKR/frFR/zhCN/zhTW
and ruRU by name.
- Container::Search picks its accent-folding set by name, as a Folding
value instead of an index switch.
- Localization::Numbers' ClientIsAsian reuses IsAsianLocale on the name.
- AddOns::TocRewrite drops its private copy of the reader, and the
slot-8 clamp, which guarded an index that cannot occur.
The DBC column reads keep using the index, which is what it is for.
---------
Co-authored-by: Brues <5278969+brues-code@users.noreply.github.com>
This commit is contained in:
+10
-7
@@ -8650,17 +8650,20 @@ enum Offsets {
|
||||
// unit-event observers. Matches nampower's `CGRaidInfo_m_raidtargets`.
|
||||
VAR_RAID_TARGET_GUIDS = 0x00B71368,
|
||||
|
||||
VAR_LOCALE_INDEX = 0x00C0E080, // 0..8, picks one of the 9 localized strings
|
||||
// 0..7: start-up stores the Wow.ini Language+Country tag's position in
|
||||
// VAR_LOCALE_NAME_TABLE (0x402562), 0 when it matches none (enGB).
|
||||
// Picks a DBC localized-string column. NOT 4.3.4's nine-slot locale
|
||||
// order — language rules go by name (Localization::Locale).
|
||||
VAR_LOCALE_INDEX = 0x00C0E080,
|
||||
|
||||
// Locale-name string table — a parallel `const char *[8]` indexed by
|
||||
// VAR_LOCALE_INDEX, holding the exact codes `GetLocale()` returns:
|
||||
// enUS / koKR / frFR / deDE / zhCN / zhTW / esES / xxYY. This is the
|
||||
// source `Script_GetLocale` (FUN_0048d8b0) reads:
|
||||
// enUS / koKR / frFR / deDE / zhCN / zhTW / esES / xxYY on a stock
|
||||
// client. This is the source `Script_GetLocale` (FUN_0048d8b0) reads:
|
||||
// lua_pushstring(L, (&PTR_DAT_008558a4)[DAT_00c0e080]);
|
||||
// Verified by resolving all 8 entries from the binary. Index range is
|
||||
// 0..7 (VAR_LOCALE_INDEX is documented 0..8, but slot 8 overreads into
|
||||
// the string pool — clamp to 0..7 before reading). Used by the TOC
|
||||
// `[TextLocale]` / `[AllowLoadTextLocale]` directives (AddOns::TocRewrite).
|
||||
// and the list SetAddOnMetadata (0x51D580) matches a TOC key's
|
||||
// `-xxXX` suffix against. Verified by resolving all 8 entries from the
|
||||
// binary. Read through Localization::Locale::Client().
|
||||
VAR_LOCALE_NAME_TABLE = 0x008558A4,
|
||||
|
||||
LUA_IS_NUMBER = 0x6F34D0,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "addons/EngineIO.h"
|
||||
#include "addons/TocRewrite.h"
|
||||
#include "addons/Toc.h"
|
||||
#include "localization/Locale.h"
|
||||
|
||||
#include "Offsets.h"
|
||||
#include "turtle/Detect.h"
|
||||
@@ -34,17 +35,7 @@ using AddOns::Toc::Lower;
|
||||
|
||||
bool IsSpace(char c) { return c == ' ' || c == '\t'; }
|
||||
|
||||
// The client locale code ("enUS", "frFR", …) — the exact string
|
||||
// GetLocale() returns (Offsets.h VAR_LOCALE_NAME_TABLE derivation).
|
||||
const char *ClientLocale() {
|
||||
uint32_t idx = *reinterpret_cast<const uint32_t *>(
|
||||
static_cast<uintptr_t>(Offsets::VAR_LOCALE_INDEX));
|
||||
if (idx > 7) idx = 0; // slot 8 overreads the string pool — clamp
|
||||
const char *const *table = reinterpret_cast<const char *const *>(
|
||||
static_cast<uintptr_t>(Offsets::VAR_LOCALE_NAME_TABLE));
|
||||
const char *s = table[idx];
|
||||
return (s != nullptr) ? s : "enUS";
|
||||
}
|
||||
using Localization::Locale::Client;
|
||||
|
||||
// True iff the comma-separated `list` (spaces allowed) contains `token` (CI).
|
||||
bool ListContains(const char *list, size_t listLen, const char *token) {
|
||||
@@ -66,7 +57,7 @@ bool ListContains(const char *list, size_t listLen, const char *token) {
|
||||
const char *VariableValue(const char *head, size_t headLen) {
|
||||
if (EqCI(head, headLen, "Family")) return "Classic";
|
||||
if (EqCI(head, headLen, "Game")) return "Vanilla";
|
||||
if (EqCI(head, headLen, "TextLocale")) return ClientLocale();
|
||||
if (EqCI(head, headLen, "TextLocale")) return Client();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -158,7 +149,7 @@ bool EvalCondition(const char *head, size_t headLen, const char *args,
|
||||
if (EqCI(head, headLen, "ExcludeLoadGameType"))
|
||||
return !ListContainsGameType(args, argsLen);
|
||||
if (EqCI(head, headLen, "AllowLoadTextLocale"))
|
||||
return ListContains(args, argsLen, ClientLocale());
|
||||
return ListContains(args, argsLen, Client());
|
||||
if (EqCI(head, headLen, "AllowLoad"))
|
||||
return ListContains(args, argsLen, "game"); // addon files: in-game only
|
||||
return false;
|
||||
|
||||
+31
-13
@@ -55,6 +55,7 @@
|
||||
#include "item/Location.h"
|
||||
#include "item/Record.h"
|
||||
#include "item/TypeInfo.h"
|
||||
#include "localization/Locale.h"
|
||||
#include "unit/Identity.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -86,12 +87,29 @@ char16_t ToLower(char16_t c) {
|
||||
return c;
|
||||
}
|
||||
|
||||
// Appends the lowercased `c` with 4.3.4's accent folding for `locale`
|
||||
// (frFR 2, deDE 3, esES/esMX 6/7, ruRU 8); æ, œ and ß become two letters.
|
||||
// Returns the new length.
|
||||
size_t AppendFolded(int locale, char16_t c, char16_t *out, size_t n) {
|
||||
switch (locale) {
|
||||
case 2: // frFR
|
||||
// 4.3.4's accent-folding sets. 4.3.4 picks one by its locale index (frFR 2,
|
||||
// deDE 3, esES/esMX 6/7, ruRU 8); 1.12's index has other slots, so the set
|
||||
// is picked by locale name.
|
||||
enum class Folding { None, French, German, Spanish, Russian };
|
||||
|
||||
Folding ClientFolding() {
|
||||
using Localization::Locale::Is;
|
||||
if (Is("frFR"))
|
||||
return Folding::French;
|
||||
if (Is("deDE"))
|
||||
return Folding::German;
|
||||
if (Is("esES") || Is("esMX"))
|
||||
return Folding::Spanish;
|
||||
if (Is("ruRU"))
|
||||
return Folding::Russian;
|
||||
return Folding::None;
|
||||
}
|
||||
|
||||
// Appends the lowercased `c` with `folding`'s accent folding; æ, œ and ß
|
||||
// become two letters. Returns the new length.
|
||||
size_t AppendFolded(Folding folding, char16_t c, char16_t *out, size_t n) {
|
||||
switch (folding) {
|
||||
case Folding::French:
|
||||
switch (c) {
|
||||
case 0xE0: case 0xE2: case 0xE4: c = u'a'; break;
|
||||
case 0xE6: out[n++] = u'a'; c = u'e'; break;
|
||||
@@ -103,14 +121,13 @@ size_t AppendFolded(int locale, char16_t c, char16_t *out, size_t n) {
|
||||
case 0xF9: case 0xFA: case 0xFB: case 0xFC: c = u'u'; break;
|
||||
}
|
||||
break;
|
||||
case 3: // deDE
|
||||
case Folding::German:
|
||||
if (c == 0xDF) {
|
||||
out[n++] = u's';
|
||||
c = u's';
|
||||
}
|
||||
break;
|
||||
case 6: // esES
|
||||
case 7: // esMX
|
||||
case Folding::Spanish:
|
||||
switch (c) {
|
||||
case 0xE1: c = u'a'; break;
|
||||
case 0xE9: c = u'e'; break;
|
||||
@@ -120,10 +137,12 @@ size_t AppendFolded(int locale, char16_t c, char16_t *out, size_t n) {
|
||||
case 0xF1: c = u'n'; break;
|
||||
}
|
||||
break;
|
||||
case 8: // ruRU
|
||||
case Folding::Russian:
|
||||
if (c == 0x451)
|
||||
c = 0x435;
|
||||
break;
|
||||
case Folding::None:
|
||||
break;
|
||||
}
|
||||
out[n++] = c;
|
||||
return n;
|
||||
@@ -141,11 +160,10 @@ size_t Fold(const char *text, char16_t (&out)[kFoldCap]) {
|
||||
// One unit short of the buffer: a full conversion stores no terminator.
|
||||
reinterpret_cast<Convert_t>(Offsets::FUN_SUNI_CONVERT_UTF8_TO_16)(
|
||||
wide, kFoldCap - 1, text, 0x7FFFFFFF, nullptr, nullptr);
|
||||
const int locale =
|
||||
*reinterpret_cast<const int *>(static_cast<uintptr_t>(Offsets::VAR_LOCALE_INDEX));
|
||||
const Folding folding = ClientFolding();
|
||||
size_t n = 0;
|
||||
for (size_t i = 0; wide[i] != 0 && n + 2 < kFoldCap; ++i)
|
||||
n = AppendFolded(locale, ToLower(wide[i]), out, n);
|
||||
n = AppendFolded(folding, ToLower(wide[i]), out, n);
|
||||
out[n] = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
## Interface: 11200
|
||||
## Title: !!!ClassicAPI
|
||||
## Notes: Library to emulate aspects of the modern WoW API on 1.12.1 / Lua 5.0.
|
||||
## Notes-ruRU: Библиотека, эмулирующая аспекты современного API WoW на 1.12.1 / Lua 5.0.
|
||||
## Version: DEV
|
||||
## Author: Brues
|
||||
## X-Website: https://github.com/brues-code/ClassicAPI
|
||||
|
||||
@@ -4,17 +4,109 @@ ACTION_BUTTON_USE_KEY_DOWN = "Способности при нажатии кл
|
||||
ACTION_TARGETING_OPTION = "Разрешить динамическое выделение";
|
||||
ADDON_MEM_KB_ABBR = "(%.0f КБ) %s";
|
||||
ADDON_MEM_MB_ABBR = "(%.2f МБ) %s";
|
||||
ADDON_OPTIONS = "Параметры модификаций";
|
||||
BINDING_NAME_AOELOOT = "Сбор добычи по области";
|
||||
BINDING_NAME_FOCUSTARGET = "Запомненная цель";
|
||||
BINDING_NAME_INTERACTMOUSEOVER = "Взаимодействовать указателем мыши";
|
||||
BINDING_NAME_INTERACTMOUSEOVER = "Взаимодействовать с объектом под курсором";
|
||||
BINDING_NAME_INTERACTTARGET = "Взаимодействовать с целью";
|
||||
BINDING_NAME_TARGETFOCUS = "Восстановить цель";
|
||||
CLASSICAPI_ACTION_BARS = "Панели команд";
|
||||
CLASSICAPI_ACTION_TARGETING = "Динамическое выделение";
|
||||
CLASSICAPI_ADVANCED = "Дополнительно";
|
||||
CLASSICAPI_ALL_SOFT_TARGET_ICONS = "Значки для всех мягких целей";
|
||||
CLASSICAPI_AUTO_DISMOUNT = "Автоматически спешиваться";
|
||||
CLASSICAPI_AUTO_DISMOUNT_TOOLTIP = "Спешиваться при применении заклинания, использовании объекта, сборе добычи, атаке или открытии карты полетов, если вы едете верхом.";
|
||||
CLASSICAPI_AUTO_STAND = "Автоматически вставать";
|
||||
CLASSICAPI_AUTO_STAND_TOOLTIP = "Вставать при применении заклинания, сборе добычи или открытии карты полетов, если вы сидите.";
|
||||
CLASSICAPI_AUTO_UNSHIFT = "Автоматически снимать облик";
|
||||
CLASSICAPI_AUTO_UNSHIFT_TOOLTIP = "Покидать свой облик при применении заклинания, которое в нем недоступно, разговоре с NPC, использовании объекта или предмета, а также при открытии карты полетов.";
|
||||
CLASSICAPI_BAGS_AND_ITEMS = "Сумки и предметы";
|
||||
CLASSICAPI_CASTING_AND_ATTACKING = "Заклинания и атаки";
|
||||
CLASSICAPI_COMBAT_LOG_RETENTION = "Хранение журнала боя";
|
||||
CLASSICAPI_COMBAT_LOG_RETENTION_TOOLTIP = "Сколько секунд журнал боя хранит записи для модификаций.";
|
||||
CLASSICAPI_CURSOR = "Курсор";
|
||||
CLASSICAPI_DIRECTION_ANY = "Любое направление";
|
||||
CLASSICAPI_DIRECTION_CONE = "В пределах 30 градусов";
|
||||
CLASSICAPI_DIRECTION_FRONT = "Прямо перед собой";
|
||||
CLASSICAPI_ENEMIES = "Противники";
|
||||
CLASSICAPI_ENEMY_DIRECTION = "Направление на противника";
|
||||
CLASSICAPI_ENEMY_ICON = "Показывать значок противника";
|
||||
CLASSICAPI_ENEMY_ICON_TOOLTIP = "Показывает значок над противником, которого выбирает динамическое выделение.";
|
||||
CLASSICAPI_ENEMY_RANGE = "Дистанция до противников";
|
||||
CLASSICAPI_EXPANDED_CURSORS = "Показывать расширенные курсоры";
|
||||
CLASSICAPI_EXPANDED_CURSORS_TOOLTIP = "При наведении на NPC или объект курсор показывает, что с ним можно сделать. Например, наковальню для ремонта, ! или ? для заданий и крючок для вашего поплавка.";
|
||||
CLASSICAPI_FRIEND_DIRECTION = "Направление на союзника";
|
||||
CLASSICAPI_FRIEND_ICON = "Показывать значок союзника";
|
||||
CLASSICAPI_FRIEND_ICON_TOOLTIP = "Показывает значок над дружественной целью, которую выбирает дружественное динамическое выделение.";
|
||||
CLASSICAPI_FRIEND_RANGE = "Дистанция до союзников";
|
||||
CLASSICAPI_FRIEND_TARGETING = "Включить дружественное динамическое выделение";
|
||||
CLASSICAPI_FRIEND_TARGETING_TOOLTIP = "Выбирает ближайшую дружественную цель в поле зрения. Если у вас нет цели, полезные заклинания применяются к ней.";
|
||||
CLASSICAPI_FRIENDS = "Союзники";
|
||||
CLASSICAPI_GAME_OBJECT_ICON = "Показывать значок взаимодействия на объектах";
|
||||
CLASSICAPI_GAME_OBJECT_ICON_TOOLTIP = "Также показывает значок взаимодействия над объектами, которые нельзя выбрать целью, например почтовыми ящиками и сундуками.";
|
||||
CLASSICAPI_HIDE_EQUIPMENT_SETS = "Скрывать комплекты экипировки на предметах";
|
||||
CLASSICAPI_HIDE_EQUIPMENT_SETS_TOOLTIP = "Убирает строку с комплектами экипировки из подсказок предметов.";
|
||||
CLASSICAPI_ICON_SIZE = "Размер значков";
|
||||
CLASSICAPI_ICON_SIZE_TOOLTIP = "Размер значков взаимодействия, противников и союзников.";
|
||||
CLASSICAPI_IGNORE_BACKPACK = "Не сортировать рюкзак";
|
||||
CLASSICAPI_IGNORE_BACKPACK_TOOLTIP = "При сортировке сумок предметы в рюкзаке остаются на своих местах.";
|
||||
CLASSICAPI_IGNORE_BANK = "Не сортировать основной банк";
|
||||
CLASSICAPI_IGNORE_BANK_TOOLTIP = "При сортировке банка предметы в основных ячейках банка остаются на своих местах.";
|
||||
CLASSICAPI_INTERACT_DIRECTION = "Направление взаимодействия";
|
||||
CLASSICAPI_INTERACT_DIRECTION_TOOLTIP = "Где должен находиться NPC или объект, чтобы его выбрала клавиша взаимодействия. Направление определяется камерой, а не персонажем.";
|
||||
CLASSICAPI_INTERACT_ICON = "Показывать значок взаимодействия";
|
||||
CLASSICAPI_INTERACT_ICON_TOOLTIP = "Показывает значок над NPC или объектом, которые использует клавиша взаимодействия.";
|
||||
CLASSICAPI_INTERACT_KEY = "Клавиша взаимодействия";
|
||||
CLASSICAPI_INTERACT_RANGE = "Дистанция взаимодействия";
|
||||
CLASSICAPI_INTERFACE = "Интерфейс";
|
||||
CLASSICAPI_KEEP_ACTION_TARGETING = "Сохранять динамическое выделение";
|
||||
CLASSICAPI_KEEP_ACTION_TARGETING_TOOLTIP = "Динамическое выделение продолжает выбирать противников и союзников, пока у вас есть цель, выбранная вручную.";
|
||||
CLASSICAPI_LOOSE_TARGET_FOLLOWS = "За кем следует обычная цель";
|
||||
CLASSICAPI_LOOSE_TARGET_FOLLOWS_ENEMY = "За мягкой целью противника";
|
||||
CLASSICAPI_LOOSE_TARGET_FOLLOWS_FRIEND = "За мягкой целью союзника";
|
||||
CLASSICAPI_LOOSE_TARGET_FOLLOWS_NONE = "Ни за кем";
|
||||
CLASSICAPI_LOOSE_TARGET_FOLLOWS_TOOLTIP = "Цель, выбранная динамическим выделением, меняется вместе с мягкой целью противника или союзника. Цель, выбранная вручную, сохраняется.";
|
||||
CLASSICAPI_LOW_PRIORITY_ICONS = "Показывать значки низкого приоритета";
|
||||
CLASSICAPI_LOW_PRIORITY_ICONS_TOOLTIP = "Также показывает значок над целью, с которой нельзя ничего сделать, например над членом группы. Значок выглядит как обычный указатель.";
|
||||
CLASSICAPI_MATCH_INSIDE_NAMES = "Учитывать правки внутри имени";
|
||||
CLASSICAPI_MATCH_INSIDE_NAMES_TOOLTIP = "Когда вы вводите текст в середине имени, предлагаемые имена также учитывают буквы после курсора.";
|
||||
CLASSICAPI_MATCH_THE_COMMAND = "Учитывать команду";
|
||||
CLASSICAPI_MATCH_THE_COMMAND_TOOLTIP = "Предлагает только имена, подходящие для команды, например участников гильдии для /gpromote. Если выключено, предлагаются все известные вам имена.";
|
||||
CLASSICAPI_MOTION = "Движение";
|
||||
CLASSICAPI_NAME_SUGGESTIONS = "Подсказки имен";
|
||||
CLASSICAPI_NAMEPLATES = "Таблички с именами";
|
||||
CLASSICAPI_NUMBERS = "Числа";
|
||||
CLASSICAPI_OPTIONS_ABOUT = "Настройки возможностей, которые ClassicAPI добавляет в игру. Каждый параметр меняет одну настройку, и изменение применяется сразу.";
|
||||
CLASSICAPI_OTHER_NAMEPLATE_ALPHA = "Непрозрачность чужих табличек";
|
||||
CLASSICAPI_OTHER_NAMEPLATE_ALPHA_TOOLTIP = "Непрозрачность табличек с именами целей, которые не являются вашей целью.";
|
||||
CLASSICAPI_PREFER_YOUR_TARGET = "Отдавать предпочтение вашей цели";
|
||||
CLASSICAPI_PREFER_YOUR_TARGET_TOOLTIP = "Если выбранную вами цель можно взять, динамическое выделение выбирает ее. Если параметр выключен, всегда выбирается ближайшая цель.";
|
||||
CLASSICAPI_RANGE_TOOLTIP = "Максимальное расстояние в ярдах. Оно не может превышать значение параметра targetNearestDistance.";
|
||||
CLASSICAPI_RECENT_WHISPERS_FIRST = "Сначала недавние шепоты";
|
||||
CLASSICAPI_RECENT_WHISPERS_FIRST_TOOLTIP = "Предлагаемые имена начинаются с игроков, которым вы шептали совсем недавно.";
|
||||
CLASSICAPI_REMEMBER_PLAYERS = "Запоминать игроков";
|
||||
CLASSICAPI_REMEMBER_PLAYERS_TOOLTIP = "Сохраняет имя, класс, расу и пол встречавшихся вам игроков, чтобы модификации могли показывать их в следующих сеансах. Данные общие для всех ваших персонажей на этом сервере.";
|
||||
CLASSICAPI_SCRIPT_PROFILE = "Профилирование скриптов";
|
||||
CLASSICAPI_SCRIPT_PROFILE_TOOLTIP = "Измеряет время процессора, которое используют модификации. Изменение вступает в силу после перезагрузки интерфейса.";
|
||||
CLASSICAPI_SEPARATE_LARGE_NUMBERS = "Разделять большие числа";
|
||||
CLASSICAPI_SEPARATE_LARGE_NUMBERS_TOOLTIP = "Ставит разделитель между группами из трех цифр, когда модификации форматируют числа с помощью BreakUpLargeNumbers.";
|
||||
CLASSICAPI_SORT_BAGS_RIGHT_TO_LEFT = "Сортировать сумки справа налево";
|
||||
CLASSICAPI_SORT_BAGS_RIGHT_TO_LEFT_TOOLTIP = "При сортировке предметы перемещаются в конец последней сумки, а хлам — в начало рюкзака.";
|
||||
CLASSICAPI_STRICT_INTERACT_RANGE = "Строгая дистанция взаимодействия";
|
||||
CLASSICAPI_STRICT_INTERACT_RANGE_TOOLTIP = "Никогда не выбирает NPC или объект за пределами дистанции взаимодействия. Если параметр выключен, объект, доступный для использования сейчас, выбирается даже на большем расстоянии.";
|
||||
CLASSICAPI_TARGET_NAMEPLATE_ALPHA = "Непрозрачность таблички цели";
|
||||
CLASSICAPI_TARGET_NAMEPLATE_ALPHA_TOOLTIP = "Непрозрачность таблички с именем вашей цели.";
|
||||
CLASSICAPI_UI_SHAKE_STRENGTH = "Сила дрожания интерфейса";
|
||||
CLASSICAPI_UI_SHAKE_STRENGTH_TOOLTIP = "Определяет, насколько сильно эффекты дрожания смещают рамки. 0 отключает дрожание.";
|
||||
CLASSICAPI_UNIT_DIRECTION_TOOLTIP = "Где должна находиться цель, чтобы ее выбрало динамическое выделение. Направление определяется камерой, а не персонажем.";
|
||||
CLASSICAPI_WHEN_YOU_HAVE_A_TARGET = "Когда у вас есть цель";
|
||||
COMBAT_LABEL = "Бой";
|
||||
COUNTDOWN_FOR_COOLDOWNS_TEXT = "Отсчет времени восстановления";
|
||||
CONFIRM_DELETE_EQUIPMENT_SET = "Вы действительно хотите удалить комплект экипировки \"%s\"?";
|
||||
CONFIRM_SAVE_EQUIPMENT_SET = "Хотите сохранить комплект экипировки \"%s\"?";
|
||||
COPPER_AMOUNT = "%d |4медная монета:медные монеты:медных монет;";
|
||||
COPPER_AMOUNT_SYMBOL = "м";
|
||||
COPPER_AMOUNT_TEXTURE = "%d|TInterface\\MoneyFrame\\UI-MoneyIcons:%d:%d:2:0:4:1:2:3:0:1|t";
|
||||
DEBUFF_SYMBOL_CURSE = "Пр";
|
||||
DEBUFF_SYMBOL_DISEASE = "Бз";
|
||||
DEBUFF_SYMBOL_MAGIC = "Мг";
|
||||
@@ -24,74 +116,66 @@ ENABLE_INTERACT_TEXT = "Включить клавишу взаимодейств
|
||||
EQUIPMENT_MANAGER = "Комплекты экипировки";
|
||||
EQUIPMENT_MANAGER_IGNORE_SLOT = "Пропустить ячейку";
|
||||
EQUIPMENT_MANAGER_IS_DISABLED = "Управление экипировкой отключено.";
|
||||
EQUIPMENT_MANAGER_ITEMS_MISSING_TOOLTIP = "%d %s |cffff0000(предмет для ячейки \"%d\" отсутствует)|r";
|
||||
EQUIPMENT_MANAGER_ITEMS_MISSING_TOOLTIP = "%d %s |cffff0000(не хватает: %d)|r";
|
||||
EQUIPMENT_MANAGER_PLACE_IN_BAGS = "Поместить в сумку";
|
||||
EQUIPMENT_MANAGER_UNIGNORE_SLOT = "Учитывать ячейку";
|
||||
EQUIPMENT_SETS = "Комплекты экипировки: |cFFFFFFFF%s|r";
|
||||
EQUIPMENT_SETS_CANT_RENAME = "Это название уже используется для другого комплекта.";
|
||||
EQUIPMENT_SETS_TOO_MANY = "Вы не можете создавать новые комплекты экипировки.";
|
||||
EQUIPMENT_SET_ASSIGN_TO_SPEC = "Выбрать специализацию:";
|
||||
EQUIPMENT_SET_ASSIGN_TO_SPEC = "Назначить специализации:";
|
||||
EQUIPMENT_SET_EDIT = "Изменить название/значок";
|
||||
EQUIPMENT_SET_SETTINGS = "Параметры";
|
||||
EQUIPSET_EQUIP = "Надеть";
|
||||
ERR_EQUIPMENT_MANAGER_BAGS_FULL = "Не удалось заменить комплект обмундирования. Инвентарь переполнен.";
|
||||
ERR_EQUIPMENT_MANAGER_COMBAT_SWAP_S = "Некоторые предметы из набора \"%s\" не будут экипированы, поскольку вы участвуете в бою.";
|
||||
ERR_EQUIPMENT_MANAGER_MISSING_ITEM_S = "Недостает одного или более предметов из набора \"%s\".";
|
||||
ERR_EQUIPMENT_MANAGER_BAGS_FULL = "Не удалось сменить экипировку. Инвентарь переполнен.";
|
||||
ERR_EQUIPMENT_MANAGER_COMBAT_SWAP_S = "Некоторые предметы из комплекта \"%s\" нельзя сменить, поскольку вы участвуете в бою.";
|
||||
ERR_EQUIPMENT_MANAGER_MISSING_ITEM_S = "Один или несколько предметов из комплекта \"%s\" отсутствуют и не были надеты.";
|
||||
EVENTTRACE_APPLY_DEFAULT_FILTER = "Применить фильтр по умолчанию";
|
||||
EVENTTRACE_ARG_FMT = "Арг. %d:";
|
||||
EVENTTRACE_BUTTON_DISABLE_FILTERS = "Снять выделение";
|
||||
EVENTTRACE_ARG_FMT = "Аргумент %d:";
|
||||
EVENTTRACE_BUTTON_DISABLE_FILTERS = "Снять все";
|
||||
EVENTTRACE_BUTTON_DISCARD_FILTER = "Сбросить все";
|
||||
EVENTTRACE_BUTTON_ENABLE_FILTERS = "Выделить все";
|
||||
EVENTTRACE_BUTTON_ENABLE_FILTERS = "Выбрать все";
|
||||
EVENTTRACE_BUTTON_MARKER = "Маркер";
|
||||
EVENTTRACE_BUTTON_PAUSE = "Пауза";
|
||||
EVENTTRACE_BUTTON_PLAY = "Играть";
|
||||
EVENTTRACE_BUTTON_PLAY = "Продолжить";
|
||||
EVENTTRACE_FILTER_HEADER = "Фильтр";
|
||||
EVENTTRACE_HEADER = "Журнал события";
|
||||
EVENTTRACE_LOG_CR_EVENTS = "Отображать глобальные события по регистрации";
|
||||
EVENTTRACE_HEADER = "Журнал событий";
|
||||
EVENTTRACE_LOG_CR_EVENTS = "Показывать события глобального реестра";
|
||||
EVENTTRACE_LOG_DISCARD = "Журнал сброшен";
|
||||
EVENTTRACE_LOG_HEADER = "Журнал";
|
||||
EVENTTRACE_LOG_PAUSE = "Регистрация участников события приостановлена";
|
||||
EVENTTRACE_LOG_PAUSE_WHILE_HIDDEN = "Фиксация событий прекращается в свернутом состоянии";
|
||||
EVENTTRACE_LOG_START = "Регистрация участников события началась";
|
||||
EVENTTRACE_LOG_WHEN_HIDDEN = "Фиксировать события в свернутом состоянии";
|
||||
EVENTTRACE_LOG_PAUSE = "Запись событий приостановлена";
|
||||
EVENTTRACE_LOG_PAUSE_WHILE_HIDDEN = "Запись событий приостановлена, пока окно скрыто";
|
||||
EVENTTRACE_LOG_START = "Запись событий начата";
|
||||
EVENTTRACE_LOG_WHEN_HIDDEN = "Записывать события, когда окно скрыто";
|
||||
EVENTTRACE_MARKER = "Маркер";
|
||||
EVENTTRACE_MESSAGE_FORMAT = "--- %s ---";
|
||||
EVENTTRACE_OPTIONS = "Параметры";
|
||||
EVENTTRACE_RESULTS = "Результаты: %d";
|
||||
EVENTTRACE_SHOW_ARGUMENTS = "Отображать аргументы события";
|
||||
EVENTTRACE_SHOW_TIMESTAMP = "Отображать метку времени события";
|
||||
EVENTTRACE_RESULTS = "Результатов: %d";
|
||||
EVENTTRACE_SHOW_ARGUMENTS = "Показывать аргументы событий";
|
||||
EVENTTRACE_SHOW_TIMESTAMP = "Показывать время событий";
|
||||
EVENTTRACE_TIMESTAMP = "Время:";
|
||||
CLEAR_FOCUS = "Отменить запоминание цели";
|
||||
INTERACT_ICONS_DEFAULT = "Только NPC (по умолчанию)";
|
||||
INTERACT_ICONS_OPTION = "Значки клавиш взаимодействия";
|
||||
INTERACT_ICONS_SHOW_ALL = "Показать все";
|
||||
INTERACT_ICONS_OPTION = "Значки клавиши взаимодействия";
|
||||
INTERACT_ICONS_SHOW_ALL = "Показывать все";
|
||||
INTERACT_ICONS_SHOW_NONE = "Не показывать";
|
||||
OPTION_TOOLTIP_ACTION_BUTTON_USE_KEY_DOWN = "Способности будут задействоваться, когда пользователь нажимает, а не отпускает клавишу.";
|
||||
OPTION_TOOLTIP_ACTION_TARGETING = "Система захвата целей, динамически выбирающая цели в поле зрения. Работает параллельно со стандартной системой наведения.";
|
||||
OPTION_TOOLTIP_COUNTDOWN_FOR_COOLDOWNS = "Отображение обратного отсчета для времени восстановления на панели команд.";
|
||||
OPTION_TOOLTIP_ENABLE_INTERACT = "Позволяет взаимодействовать с NPC и объектами по нажатию клавиши.";
|
||||
OPTION_TOOLTIP_INTERACT_ICONS = "Определяет, какие значки появляются над NPC и объектами, если включен параметр клавиши взаимодействия.";
|
||||
OPTION_TOOLTIP_STOP_AUTO_ATTACK = "Прекращение автоматической атаки при смене цели.";
|
||||
OPTION_TOOLTIP_ACTION_BUTTON_USE_KEY_DOWN = "Способности на панели команд срабатывают при нажатии клавиши, а не при ее отпускании.";
|
||||
OPTION_TOOLTIP_ACTION_TARGETING = "Система выделения, которая динамически выбирает противников в зависимости от направления взгляда. Работает вместе со стандартной системой выделения.";
|
||||
OPTION_TOOLTIP_COUNTDOWN_FOR_COOLDOWNS = "Показывать числовой отсчет времени восстановления на панели команд.";
|
||||
OPTION_TOOLTIP_ENABLE_INTERACT = "Позволяет взаимодействовать с NPC и объектами нажатием клавиши.";
|
||||
OPTION_TOOLTIP_INTERACT_ICONS = "Определяет, какие значки появляются над NPC и объектами, когда включена клавиша взаимодействия.";
|
||||
OPTION_TOOLTIP_STOP_AUTO_ATTACK = "Прекращать автоатаку при смене цели.";
|
||||
KEY_BINDING_NAME_AND_KEY = "%s (%s)";
|
||||
KEY_BINDING_TOOLTIP = "<ПКМ, чтобы отвязать>";
|
||||
SETTINGS_BIND_KEY_TO_COMMAND_OR_CANCEL = "Назначьте клавишу \"%s\"или нажмите %s для отмены";
|
||||
KEY_BINDING_TOOLTIP = "<ПКМ, чтобы снять привязку>";
|
||||
SETTINGS_BIND_KEY_TO_COMMAND_OR_CANCEL = "Назначьте клавишу для \"%s\" или нажмите %s для отмены";
|
||||
SET_FOCUS = "Запомнить цель";
|
||||
FOCUS_TOKEN_NOT_FOUND = "<фокус отсутствует>";
|
||||
FOCUSTARGET = "Запомнить цель";
|
||||
ITEMS_VARIABLE_QUANTITY = "%d |4предмет:предмета:предметов;";
|
||||
ITEMS_EQUIPPED = "%d |4предмет экипирован:предмета экипировано:предметов экипировано;";
|
||||
ITEMS_IN_INVENTORY = "%d |4предмет:предмета:предметов; в сумках";
|
||||
ITEM_SLOTS_IGNORED = "%d |4ячейка пропущена:ячейки пропущены:ячейки пропущены;";
|
||||
ITEM_MISSING = "Предмет для ячейки %s отсутствует";
|
||||
ITEMS_NOT_IN_INVENTORY = "%d |4предмет отсутствует:предмета отсутствует:предметов отсутствуют;";
|
||||
STOP_AUTO_ATTACK = "Прекращение автоатаки";
|
||||
TARGETFOCUS = "Восстановить цель";
|
||||
-- BINDING_NAME_AOELOOT = "AoE Loot";
|
||||
COPPER_AMOUNT = "%d |4медная монета:медные монеты:медных монет;";
|
||||
COPPER_AMOUNT_SYMBOL = "м";
|
||||
GEARSETS_POPUP_TEXT = "Введите название комплекта (не более 16 символов):";
|
||||
GENERIC_FRACTION_STRING = "%d/%d";
|
||||
GOLD_AMOUNT = "%d |4золотая:золотые:золотых;";
|
||||
GOLD_AMOUNT_SYMBOL = "з";
|
||||
GOLD_AMOUNT_TEXTURE = "%d|TInterface\\MoneyFrame\\UI-MoneyIcons:%d:%d:2:0:4:1:0:1:0:1|t";
|
||||
GOLD_AMOUNT_TEXTURE_STRING = "%s|TInterface\\MoneyFrame\\UI-MoneyIcons:%d:%d:2:0:4:1:0:1:0:1|t";
|
||||
HOURS_MINUTES_SECONDS = "%.2d:%.2d:%.2d";
|
||||
ICON_FILTER_ALL = "Все значки";
|
||||
ICON_FILTER_ITEM = "Предметы";
|
||||
@@ -109,17 +193,17 @@ ITEM_MOD_ATTACK_POWER_SHORT = "к силе атаки";
|
||||
ITEM_MOD_RANGED_ATTACK_POWER_SHORT = "к силе атаки дальнего боя";
|
||||
ITEM_MOD_CRIT_MELEE_RATING_SHORT = "к показателю критического удара в ближнем бою";
|
||||
ITEM_MOD_CRIT_RANGED_RATING_SHORT = "к показателю критического удара в дальнем бою";
|
||||
ITEM_MOD_HIT_MELEE_RATING_SHORT = "к меткости в ближнем бою";
|
||||
ITEM_MOD_HIT_MELEE_RATING_SHORT = "к меткости в ближнем бою";
|
||||
ITEM_MOD_HIT_RANGED_RATING_SHORT = "к меткости в дальнем бою";
|
||||
ITEM_MOD_HIT_SPELL_RATING_SHORT = "к меткости заклинаний";
|
||||
ITEM_MOD_CRIT_SPELL_RATING_SHORT = "к критическому эффекту заклинаний";
|
||||
ITEM_MOD_SPELL_DAMAGE_DONE_SHORT = "к дополнительному урону";
|
||||
ITEM_MOD_SPELL_HEALING_DONE_SHORT = "к дополнительному исцелению";
|
||||
ITEM_MOD_MANA_REGENERATION_SHORT = "к восполнению маны";
|
||||
ITEM_MOD_DEFENSE_SKILL_RATING_SHORT = "Защита";
|
||||
ITEM_MOD_DODGE_RATING_SHORT = "Уклонение";
|
||||
ITEM_MOD_PARRY_RATING_SHORT = "Парирование";
|
||||
ITEM_MOD_BLOCK_RATING_SHORT = "Блок";
|
||||
ITEM_MOD_DEFENSE_SKILL_RATING_SHORT = "к защите";
|
||||
ITEM_MOD_DODGE_RATING_SHORT = "к уклонению";
|
||||
ITEM_MOD_PARRY_RATING_SHORT = "к парированию";
|
||||
ITEM_MOD_BLOCK_RATING_SHORT = "к блокированию";
|
||||
ITEM_MOD_BLOCK_VALUE_SHORT = "к показателю блокирования";
|
||||
ITEM_MOD_HOLY_SPELL_DAMAGE_DONE_SHORT = "к урону от магии света";
|
||||
ITEM_MOD_FIRE_SPELL_DAMAGE_DONE_SHORT = "к урону от магии огня";
|
||||
@@ -131,6 +215,12 @@ ITEM_MOD_SPELL_PENETRATION_SHORT = "к проникающей способнос
|
||||
ITEM_MOD_ARMOR_PENETRATION_RATING_SHORT = "к пробиванию брони";
|
||||
ITEM_MOD_HEALTH_REGEN_SHORT = "к восполнению здоровья";
|
||||
ITEM_MOD_DAMAGE_PER_SECOND_SHORT = "к урону в секунду";
|
||||
ITEMS_EQUIPPED = "%d |4предмет экипирован:предмета экипировано:предметов экипировано;";
|
||||
ITEMS_IN_INVENTORY = "%d |4предмет:предмета:предметов; в инвентаре";
|
||||
ITEMS_NOT_IN_INVENTORY = "%d |4предмет отсутствует:предмета отсутствует:предметов отсутствует;";
|
||||
ITEMS_VARIABLE_QUANTITY = "%d |4предмет:предмета:предметов;";
|
||||
ITEM_MISSING = "%s отсутствует";
|
||||
ITEM_SLOTS_IGNORED = "%d |4ячейка пропущена:ячейки пропущены:ячеек пропущено;";
|
||||
LARGE_NUMBER_SEPERATOR = "";
|
||||
DECIMAL_SEPERATOR = ",";
|
||||
FIRST_NUMBER = "тыс.";
|
||||
@@ -141,14 +231,18 @@ SECOND_NUMBER_CAP = "М";
|
||||
SECOND_NUMBER_CAP_NO_SPACE = "М";
|
||||
THIRD_NUMBER = "млрд";
|
||||
FOURTH_NUMBER = "трлн";
|
||||
LUA_ERROR = "Ошибка Lua";
|
||||
MINUTES_SECONDS = "%.2d:%.2d";
|
||||
PAPERDOLL_EQUIPMENTMANAGER = "Комплекты экипировки";
|
||||
PAPERDOLL_NEWEQUIPMENTSET = "Новый комплект";
|
||||
PERCENTAGE_STRING = "%d%%";
|
||||
SET_FRIENDNOTE_LABEL = "Заметка о |3-5(%s):";
|
||||
PLAYER_LIST_DELIMITER = ", ";
|
||||
RELOADUI = "Перезагрузить интерфейс";
|
||||
SET_FRIENDNOTE_LABEL = "Задать заметку для %s:";
|
||||
SET_NOTE = "Добавить заметку";
|
||||
SILVER_AMOUNT = "%d |4серебряная:серебряные:серебряных;";
|
||||
SILVER_AMOUNT_SYMBOL = "с";
|
||||
SILVER_AMOUNT_TEXTURE = "%d|TInterface\\MoneyFrame\\UI-MoneyIcons:%d:%d:2:0:4:1:1:2:0:1|t";
|
||||
SLASH_CANCELAURA1 = "/снятьауру";
|
||||
SLASH_CANCELAURA2 = "/cancelaura";
|
||||
SLASH_CANCELFORM1 = "/нормоблик";
|
||||
@@ -222,10 +316,14 @@ SLASH_TARGET_NEAREST_RAID2 = "/targetraid";
|
||||
SLASH_USE1 = "/use";
|
||||
SLASH_USE2 = "/использовать";
|
||||
SOCIAL_LABEL = "Общение";
|
||||
LINK_TRADESKILL_TOOLTIP = "Щелкните здесь, чтобы создать ссылку на вашу профессию.";
|
||||
TEXT_TO_SPEECH = "Текст в речь";
|
||||
TEXT_TO_SPEECH_ADJUST_RATE = "Изменить скорость речи.";
|
||||
STOP_AUTO_ATTACK = "Прекращение автоатаки";
|
||||
SYMBOLIC_NEGATIVE_NUMBER = "-%s";
|
||||
SYMBOLIC_POSITIVE_NUMBER = "+%s";
|
||||
TARGETFOCUS = "Восстановить цель";
|
||||
LINK_TRADESKILL_TOOLTIP = "Щелкните здесь, чтобы создать ссылку на свою профессию.";
|
||||
TEXT_TO_SPEECH = "Синтез речи";
|
||||
TEXT_TO_SPEECH_ADJUST_RATE = "Изменить скорость речи";
|
||||
TEXT_TO_SPEECH_ADJUST_VOLUME = "Громкость речи";
|
||||
TEXT_TO_SPEECH_VOICE_OPTION = "Параметры голоса";
|
||||
TOTAL_MEM_KB_ABBR = "Память: %.0f КБ";
|
||||
TOTAL_MEM_MB_ABBR = "Модификации занимают в памяти: %.2f МБ";
|
||||
TEXT_TO_SPEECH_VOICE_OPTION = "Выбор голоса";
|
||||
TOTAL_MEM_KB_ABBR = "Память модификаций: %.0f КБ";
|
||||
TOTAL_MEM_MB_ABBR = "Память модификаций: %.2f МБ";
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "localization/Locale.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
namespace Localization::Locale {
|
||||
|
||||
// Script_GetLocale (FUN_0048d8b0): VAR_LOCALE_NAME_TABLE[VAR_LOCALE_INDEX].
|
||||
const char *Client() {
|
||||
const uint32_t index = Game::Read<uint32_t>(Offsets::VAR_LOCALE_INDEX);
|
||||
return reinterpret_cast<const char *const *>(
|
||||
static_cast<uintptr_t>(Offsets::VAR_LOCALE_NAME_TABLE))[index];
|
||||
}
|
||||
|
||||
bool Is(const char *code) { return std::strcmp(Client(), code) == 0; }
|
||||
|
||||
} // namespace Localization::Locale
|
||||
@@ -0,0 +1,32 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
// The client locale by NAME, as GetLocale() reports it.
|
||||
//
|
||||
// Language rules (plurals, accent folding, number abbreviations) must pick
|
||||
// by this name, never by VAR_LOCALE_INDEX. The 1.12 index is the Wow.ini
|
||||
// tag's position in a table of eight codes (enUS koKR frFR deDE zhCN zhTW
|
||||
// esES xxYY), not 4.3.4's nine-slot order, so a 4.3.4 index such as 8 for
|
||||
// ruRU never occurs here. A Russian client is one whose table holds "ruRU",
|
||||
// in whatever slot. The index stays right for picking a DBC string column.
|
||||
namespace Localization::Locale {
|
||||
|
||||
// The locale code GetLocale() returns ("enUS", "frFR", ...).
|
||||
const char *Client();
|
||||
|
||||
// Whether the client locale is `code`.
|
||||
bool Is(const char *code);
|
||||
|
||||
} // namespace Localization::Locale
|
||||
@@ -62,6 +62,7 @@
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
#include "cvar/Factory.h"
|
||||
#include "localization/Locale.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
@@ -131,17 +132,13 @@ constexpr Scale kScalesAsian[] = {
|
||||
|
||||
CVar::Factory::Handle g_breakUpLargeNumbers = nullptr;
|
||||
|
||||
// VAR_LOCALE_INDEX order: enUS, koKR, frFR, deDE, zhCN, zhTW, esES, esMX, ruRU.
|
||||
bool ClientIsAsian() {
|
||||
const int locale = Game::Read<int>(Offsets::VAR_LOCALE_INDEX);
|
||||
return locale == 1 || locale == 4 || locale == 5;
|
||||
}
|
||||
|
||||
bool IsAsianLocale(const char *locale) {
|
||||
return std::strcmp(locale, "koKR") == 0 || std::strcmp(locale, "zhCN") == 0 ||
|
||||
std::strcmp(locale, "zhTW") == 0;
|
||||
}
|
||||
|
||||
bool ClientIsAsian() { return IsAsianLocale(Localization::Locale::Client()); }
|
||||
|
||||
// `_G[name]` when it is a string, else "".
|
||||
std::string GlobalString(void *L, const char *name) {
|
||||
Lua::PushString(L, name);
|
||||
|
||||
+7
-10
@@ -31,6 +31,7 @@
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
#include "localization/Locale.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
@@ -45,16 +46,13 @@ using Rule_t = char *(__fastcall *)(const char *text);
|
||||
constexpr size_t kBufferSize = 0x2000;
|
||||
char g_buffer[kBufferSize];
|
||||
|
||||
// FrameScript_GetPluralIndex. The 1.12 locale index has 4.3.4's order for
|
||||
// 0..7 (enUS, koKR, frFR, deDE, zhCN, zhTW, esES, esMX); 8 is ruRU there.
|
||||
// FrameScript_GetPluralIndex, keyed by locale name: 4.3.4 switches on its own
|
||||
// locale index, whose slots 1.12's index does not share (Localization::Locale).
|
||||
uint32_t PluralIndex(int32_t count) {
|
||||
switch (Game::Read<int>(Offsets::VAR_LOCALE_INDEX)) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 5:
|
||||
using Localization::Locale::Is;
|
||||
if (Is("koKR") || Is("frFR") || Is("zhCN") || Is("zhTW"))
|
||||
return count > 1;
|
||||
case 8:
|
||||
if (Is("ruRU")) {
|
||||
if (count % 100 >= 11 && count % 100 <= 14)
|
||||
return 2;
|
||||
if (count % 10 == 1)
|
||||
@@ -62,9 +60,8 @@ uint32_t PluralIndex(int32_t count) {
|
||||
if (count % 10 >= 2 && count % 10 <= 4)
|
||||
return 1;
|
||||
return 2;
|
||||
default:
|
||||
return count != 1;
|
||||
}
|
||||
return count != 1;
|
||||
}
|
||||
|
||||
bool IsDigit(char ch) { return ch >= '0' && ch <= '9'; }
|
||||
|
||||
Reference in New Issue
Block a user