Browse Source

add my changes from #3603

pull/3587/head
maufeat 2 weeks ago
parent
commit
b25889bf6b
  1. 5
      src/android/app/src/main/res/values/arrays.xml
  2. 4
      src/android/app/src/main/res/values/strings.xml
  3. 2
      src/common/settings_enums.h
  4. 147
      src/core/file_sys/control_metadata.cpp
  5. 57
      src/core/file_sys/control_metadata.h
  6. 10
      src/core/hle/service/ns/language.cpp
  7. 13
      src/core/hle/service/set/settings_types.h
  8. 1
      src/qt_common/config/shared_translation.cpp

5
src/android/app/src/main/res/values/arrays.xml

@ -64,7 +64,8 @@
<item>@string/language_spanish</item> <item>@string/language_spanish</item>
<item>@string/language_taiwanese</item> <item>@string/language_taiwanese</item>
<item>@string/language_traditional_chinese</item> <item>@string/language_traditional_chinese</item>
<item>@string/language_serbian</item>
<item>@string/language_polish</item>
<item>@string/language_thai</item>
</string-array> </string-array>
<integer-array name="languageValues"> <integer-array name="languageValues">
@ -87,6 +88,7 @@
<item>11</item> <item>11</item>
<item>16</item> <item>16</item>
<item>18</item> <item>18</item>
<item>19</item>
</integer-array> </integer-array>
<string-array name="rendererApiNames"> <string-array name="rendererApiNames">
@ -407,6 +409,7 @@
<item>@string/app_language_persian</item> <item>@string/app_language_persian</item>
<item>@string/app_language_hebrew</item> <item>@string/app_language_hebrew</item>
<item>@string/app_language_serbian</item> <item>@string/app_language_serbian</item>
<item>@string/app_language_thai</item>
</string-array> </string-array>
<integer-array name="appLanguageValues"> <integer-array name="appLanguageValues">
<item>0</item> <item>0</item>

4
src/android/app/src/main/res/values/strings.xml

@ -1011,7 +1011,8 @@
<string name="language_simplified_chinese" translatable="false">简体中文</string> <string name="language_simplified_chinese" translatable="false">简体中文</string>
<string name="language_traditional_chinese" translatable="false">正體中文</string> <string name="language_traditional_chinese" translatable="false">正體中文</string>
<string name="language_brazilian_portuguese" translatable="false">Português do Brasil</string> <string name="language_brazilian_portuguese" translatable="false">Português do Brasil</string>
<string name="language_serbian" translatable="false">српски</string>
<string name="language_polish" translatable="false">Polska</string>
<string name="language_thai" translatable="false">แบบไทย</string>
<!-- Memory Sizes --> <!-- Memory Sizes -->
<string name="memory_byte_shorthand">B</string> <string name="memory_byte_shorthand">B</string>
@ -1216,6 +1217,7 @@
<string name="app_language_persian" translatable="false">فارسی</string> <string name="app_language_persian" translatable="false">فارسی</string>
<string name="app_language_hebrew" translatable="false">עברית</string> <string name="app_language_hebrew" translatable="false">עברית</string>
<string name="app_language_serbian" translatable="false">Српски</string> <string name="app_language_serbian" translatable="false">Српски</string>
<string name="app_language_thai" translatable="false">แบบไทย</string>
<!-- Static Themes --> <!-- Static Themes -->
<string name="static_theme_color">Theme Color</string> <string name="static_theme_color">Theme Color</string>

2
src/common/settings_enums.h

@ -120,7 +120,7 @@ ENUM(AudioMode, Mono, Stereo, Surround);
ENUM(Language, Japanese, EnglishAmerican, French, German, Italian, Spanish, Chinese, Korean, Dutch, ENUM(Language, Japanese, EnglishAmerican, French, German, Italian, Spanish, Chinese, Korean, Dutch,
Portuguese, Russian, Taiwanese, EnglishBritish, FrenchCanadian, SpanishLatin, Portuguese, Russian, Taiwanese, EnglishBritish, FrenchCanadian, SpanishLatin,
ChineseSimplified, ChineseTraditional, PortugueseBrazilian, Serbian, Polish, Thai);
ChineseSimplified, ChineseTraditional, PortugueseBrazilian, Polish, Thai);
ENUM(Region, Japan, Usa, Europe, Australia, China, Korea, Taiwan); ENUM(Region, Japan, Usa, Europe, Australia, China, Korea, Taiwan);
ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt, Gb, GbEire, Gmt, ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt, Gb, GbEire, Gmt,
GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica, GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,

147
src/core/file_sys/control_metadata.cpp

@ -41,71 +41,37 @@ const std::array<const char*, size_t(Language::Count)> LANGUAGE_NAMES{{
"Thai", "Thai",
}}; }};
namespace {
constexpr std::size_t LEGACY_LANGUAGE_REGION_SIZE = sizeof(std::array<LanguageEntry, 16>);
constexpr std::size_t PACKED_LANGUAGE_REGION_MAX_SIZE = sizeof(LanguageEntry) * 32;
namespace
{
constexpr std::size_t MAX_EXPANDED_LANG_SIZE = sizeof(LanguageEntry) * 32;
bool InflateRawDeflate(std::span<const u8> compressed, std::vector<u8>& out) {
if (compressed.empty() || compressed.size() > std::numeric_limits<uInt>::max()) {
return false;
}
z_stream stream{};
stream.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(compressed.data()));
stream.avail_in = static_cast<uInt>(compressed.size());
if (inflateInit2(&stream, -MAX_WBITS) != Z_OK) {
return false;
}
bool InflateRawDeflate(std::span<const u8> compressed, std::vector<u8>& out)
{
if (compressed.empty()) return false;
std::array<u8, 0x1000> chunk{};
int ret = Z_OK;
while (ret == Z_OK) {
stream.next_out = reinterpret_cast<Bytef*>(chunk.data());
stream.avail_out = static_cast<uInt>(chunk.size());
ret = inflate(&stream, Z_NO_FLUSH);
if (ret != Z_OK && ret != Z_STREAM_END) {
inflateEnd(&stream);
z_stream stream{};
stream.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(compressed.data()));
stream.avail_in = static_cast<uInt>(compressed.size());
if (inflateInit2(&stream, -MAX_WBITS) != Z_OK) {
return false; return false;
} }
const auto produced = chunk.size() - static_cast<std::size_t>(stream.avail_out);
if (produced != 0) {
if (out.size() + produced > PACKED_LANGUAGE_REGION_MAX_SIZE) {
inflateEnd(&stream);
return false;
}
out.insert(out.end(), chunk.begin(),
chunk.begin() + static_cast<std::ptrdiff_t>(produced));
}
}
out.resize(MAX_EXPANDED_LANG_SIZE);
stream.next_out = reinterpret_cast<Bytef*>(out.data());
stream.avail_out = static_cast<uInt>(out.size());
inflateEnd(&stream);
return ret == Z_STREAM_END;
}
int ret = inflate(&stream, Z_FINISH);
inflateEnd(&stream);
void DecodePackedLanguageEntries(RawNACP& raw) {
auto* packed_region = reinterpret_cast<u8*>(raw.language_entries.data());
u16_le compressed_size_le{};
std::memcpy(&compressed_size_le, packed_region, sizeof(compressed_size_le));
const auto compressed_size = static_cast<std::size_t>(compressed_size_le);
if (compressed_size == 0 || compressed_size > LEGACY_LANGUAGE_REGION_SIZE - sizeof(u16_le)) {
return;
}
std::vector<u8> decompressed;
if (!InflateRawDeflate(
std::span<const u8>(packed_region + sizeof(u16_le), compressed_size), decompressed)) {
return;
}
if (ret != Z_STREAM_END && ret != Z_OK) {
return false;
}
if (decompressed.size() < LEGACY_LANGUAGE_REGION_SIZE ||
decompressed.size() % sizeof(LanguageEntry) != 0) {
return;
// Shrink to actual decompressed size
out.resize(stream.total_out);
return true;
} }
std::memcpy(raw.language_entries.data(), decompressed.data(), LEGACY_LANGUAGE_REGION_SIZE);
}
} // namespace } // namespace
std::string LanguageEntry::GetApplicationName() const { std::string LanguageEntry::GetApplicationName() const {
@ -141,55 +107,48 @@ constexpr std::array<Language, 20> language_to_codes = {{
NACP::NACP() = default; NACP::NACP() = default;
NACP::NACP(VirtualFile file) {
NACP::NACP(VirtualFile file)
{
file->ReadObject(&raw); file->ReadObject(&raw);
DecodePackedLanguageEntries(raw);
if (raw.titles_data_format == TitleDataFormat::Compressed) {
const u16 compressed_size = raw.language_entries.compressed_data.buffer_size;
std::span<const u8> compressed_payload{raw.language_entries.compressed_data.buffer,
compressed_size};
std::vector<u8> decompressed;
if (InflateRawDeflate(compressed_payload, decompressed)) {
const size_t entry_count = decompressed.size() / sizeof(LanguageEntry);
language_entries.resize(entry_count);
std::memcpy(language_entries.data(), decompressed.data(), decompressed.size());
}
} else {
language_entries.resize(16);
std::memcpy(language_entries.data(), raw.language_entries.language_entries.data(),
sizeof(raw.language_entries.language_entries));
}
} }
NACP::~NACP() = default; NACP::~NACP() = default;
const LanguageEntry& NACP::GetLanguageEntry() const { const LanguageEntry& NACP::GetLanguageEntry() const {
auto const language = []{
switch (Settings::values.language_index.GetValue()) {
case Settings::Language::Chinese: return Language::SimplifiedChinese;
case Settings::Language::ChineseSimplified: return Language::SimplifiedChinese;
case Settings::Language::ChineseTraditional: return Language::TraditionalChinese;
case Settings::Language::Dutch: return Language::Dutch;
case Settings::Language::EnglishAmerican: return Language::AmericanEnglish;
case Settings::Language::EnglishBritish: return Language::BritishEnglish;
case Settings::Language::French: return Language::French;
case Settings::Language::FrenchCanadian: return Language::CanadianFrench;
case Settings::Language::German: return Language::German;
case Settings::Language::Italian: return Language::Italian;
case Settings::Language::Korean: return Language::Korean;
case Settings::Language::Japanese: return Language::Japanese;
case Settings::Language::Portuguese: return Language::Portuguese;
case Settings::Language::PortugueseBrazilian: return Language::BrazilianPortuguese;
case Settings::Language::Russian: return Language::Russian;
case Settings::Language::Serbian: return Language::Russian;
case Settings::Language::Spanish: return Language::Spanish;
case Settings::Language::SpanishLatin: return Language::LatinAmericanSpanish;
case Settings::Language::Taiwanese: return Language::SimplifiedChinese;
case Settings::Language::Thai: return Language::Thai;
case Settings::Language::Polish: return Language::Polish;
}
}();
{
const auto& language_entry = raw.language_entries.at(static_cast<u8>(language));
if (!language_entry.GetApplicationName().empty())
return language_entry;
u32 index = static_cast<u32>(Settings::values.language_index.GetValue());
if (index < language_entries.size()) {
return language_entries[index];
} }
for (const auto& language_entry : raw.language_entries) {
if (!language_entry.GetApplicationName().empty())
return language_entry;
for (const auto& entry : language_entries) {
return entry;
} }
return raw.language_entries.at(static_cast<u8>(Language::AmericanEnglish));
return language_entries.at(static_cast<u8>(Language::AmericanEnglish));
} }
std::array<std::string, 16> NACP::GetApplicationNames() const {
std::array<std::string, 16> names{};
for (size_t i = 0; i < raw.language_entries.size(); ++i) {
names[i] = raw.language_entries[i].GetApplicationName();
std::vector<std::string> NACP::GetApplicationNames() const {
std::vector<std::string> names;
names.reserve(language_entries.size());
for (const auto& entry : language_entries) {
names.push_back(entry.GetApplicationName());
} }
return names; return names;
} }

57
src/core/file_sys/control_metadata.h

@ -27,6 +27,24 @@ struct LanguageEntry {
}; };
static_assert(sizeof(LanguageEntry) == 0x300); static_assert(sizeof(LanguageEntry) == 0x300);
struct LanguageEntryData {
union {
// TitleDataFormat::Uncompressed (16 entries)
std::array<LanguageEntry, 16> language_entries;
// TitleDataFormat::Compressed (18+ entries)
struct {
u16 buffer_size;
u8 buffer[0x2FFE];
} compressed_data;
};
};
enum class TitleDataFormat : u8 {
Uncompressed = 0,
Compressed = 1,
};
struct ApplicationNeighborDetectionGroupConfiguration { struct ApplicationNeighborDetectionGroupConfiguration {
u64 group_id; ///< GroupId u64 group_id; ///< GroupId
std::array<u8, 0x10> key; std::array<u8, 0x10> key;
@ -98,6 +116,17 @@ struct AccessibleLaunchRequiredVersion {
}; };
static_assert(sizeof(AccessibleLaunchRequiredVersion) == 0x40); static_assert(sizeof(AccessibleLaunchRequiredVersion) == 0x40);
enum class CrashReport : u8 {
Deny = 0,
Allow = 1,
};
enum class PlayLogQueryCapability : u8 {
None = 0,
WhiteList = 1,
All = 2,
};
struct ApplicationControlDataConditionData { struct ApplicationControlDataConditionData {
u8 priority; u8 priority;
INSERT_PADDING_BYTES(7); INSERT_PADDING_BYTES(7);
@ -154,7 +183,7 @@ enum class SupportedLanguage : u32 {
// The raw file format of a NACP file. // The raw file format of a NACP file.
struct RawNACP { struct RawNACP {
std::array<LanguageEntry, 16> language_entries;
LanguageEntryData language_entries;
std::array<u8, 0x25> isbn; std::array<u8, 0x25> isbn;
u8 startup_user_account; u8 startup_user_account;
u8 user_account_switch_lock; u8 user_account_switch_lock;
@ -165,7 +194,7 @@ struct RawNACP {
bool screenshot_enabled; bool screenshot_enabled;
u8 video_capture_mode; u8 video_capture_mode;
bool data_loss_confirmation; bool data_loss_confirmation;
INSERT_PADDING_BYTES(1);
u8 play_log_policy;
u64_le presence_group_id; u64_le presence_group_id;
std::array<u8, 0x20> rating_age; std::array<u8, 0x20> rating_age;
std::array<char, 0x10> version_string; std::array<char, 0x10> version_string;
@ -181,10 +210,14 @@ struct RawNACP {
u8 logo_type; u8 logo_type;
u8 logo_handling; u8 logo_handling;
bool runtime_add_on_content_install; bool runtime_add_on_content_install;
INSERT_PADDING_BYTES(5);
u8 runtime_parameter_delivery;
u8 appropriate_age_for_china;
INSERT_PADDING_BYTES(1);
CrashReport crash_report;
u64_le seed_for_pseudo_device_id; u64_le seed_for_pseudo_device_id;
std::array<u8, 0x41> bcat_passphrase; std::array<u8, 0x41> bcat_passphrase;
INSERT_PADDING_BYTES(7);
u8 startup_user_account_option;
INSERT_PADDING_BYTES(6); // ReservedForUserAccountSaveDataOperation
u64_le user_account_save_data_max_size; u64_le user_account_save_data_max_size;
u64_le user_account_save_data_max_journal_size; u64_le user_account_save_data_max_journal_size;
u64_le device_save_data_max_size; u64_le device_save_data_max_size;
@ -194,9 +227,15 @@ struct RawNACP {
u64_le cache_storage_journal_size; u64_le cache_storage_journal_size;
u64_le cache_storage_data_and_journal_max_size; u64_le cache_storage_data_and_journal_max_size;
u16_le cache_storage_max_index; u16_le cache_storage_max_index;
INSERT_PADDING_BYTES(0x8B);
u8 runtime_upgrade;
u32_le supporting_limited_application_licenses;
std::array<u8, 0x8*16> play_log_queryable_application_id;
PlayLogQueryCapability play_log_query_capability;
u8 repair_flag;
u8 program_index;
u8 required_network_service_license_on_launch_flag;
u8 app_error_code_prefix; u8 app_error_code_prefix;
u8 title_compression;
TitleDataFormat titles_data_format;
u8 acd_index; u8 acd_index;
ApparentPlatform apparent_platform; ApparentPlatform apparent_platform;
// NeighborDetectionClientConfiguration neighbor_detection_client_configuration; // NeighborDetectionClientConfiguration neighbor_detection_client_configuration;
@ -219,7 +258,8 @@ struct RawNACP {
u8 has_ingame_voice_chat; u8 has_ingame_voice_chat;
INSERT_PADDING_BYTES(3); INSERT_PADDING_BYTES(3);
u32_le supported_extra_addon_content_flag; u32_le supported_extra_addon_content_flag;
INSERT_PADDING_BYTES(0x698);
u8 has_karaoke_feature;
INSERT_PADDING_BYTES(0x697);
std::array<u8, 0x400> platform_specific_region; std::array<u8, 0x400> platform_specific_region;
}; };
static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size."); static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");
@ -243,7 +283,7 @@ public:
u64 GetDefaultNormalSaveSize() const; u64 GetDefaultNormalSaveSize() const;
u64 GetDefaultJournalSaveSize() const; u64 GetDefaultJournalSaveSize() const;
u32 GetSupportedLanguages() const; u32 GetSupportedLanguages() const;
std::array<std::string, 16> GetApplicationNames() const;
std::vector<std::string> GetApplicationNames() const;
std::vector<u8> GetRawBytes() const; std::vector<u8> GetRawBytes() const;
bool GetUserAccountSwitchLock() const; bool GetUserAccountSwitchLock() const;
u64 GetDeviceSaveDataSize() const; u64 GetDeviceSaveDataSize() const;
@ -252,6 +292,7 @@ public:
private: private:
RawNACP raw{}; RawNACP raw{};
std::vector<LanguageEntry> language_entries;
}; };
} // namespace FileSys } // namespace FileSys

10
src/core/hle/service/ns/language.cpp

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -329,9 +332,6 @@ constexpr ApplicationLanguagePriorityList priority_list_brazilian_portuguese = {
constexpr ApplicationLanguagePriorityList priority_list_thai = {{ constexpr ApplicationLanguagePriorityList priority_list_thai = {{
ApplicationLanguage::Thai, ApplicationLanguage::Thai,
ApplicationLanguage::BrazilianPortuguese,
ApplicationLanguage::Portuguese,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::AmericanEnglish, ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::BritishEnglish, ApplicationLanguage::BritishEnglish,
ApplicationLanguage::Japanese, ApplicationLanguage::Japanese,
@ -350,10 +350,6 @@ constexpr ApplicationLanguagePriorityList priority_list_thai = {{
constexpr ApplicationLanguagePriorityList priority_list_polish = {{ constexpr ApplicationLanguagePriorityList priority_list_polish = {{
ApplicationLanguage::Polish, ApplicationLanguage::Polish,
ApplicationLanguage::Thai,
ApplicationLanguage::BrazilianPortuguese,
ApplicationLanguage::Portuguese,
ApplicationLanguage::LatinAmericanSpanish,
ApplicationLanguage::AmericanEnglish, ApplicationLanguage::AmericanEnglish,
ApplicationLanguage::BritishEnglish, ApplicationLanguage::BritishEnglish,
ApplicationLanguage::Japanese, ApplicationLanguage::Japanese,

13
src/core/hle/service/set/settings_types.h

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
@ -193,8 +196,8 @@ enum class LanguageCode : u64 {
ZH_HANS = 0x00736E61482D687A, ZH_HANS = 0x00736E61482D687A,
ZH_HANT = 0x00746E61482D687A, ZH_HANT = 0x00746E61482D687A,
PT_BR = 0x00000052422D7470, PT_BR = 0x00000052422D7470,
TH = 0x0000000000006874,
PL = 0x000000000000706C, PL = 0x000000000000706C,
TH = 0x0000000000006874,
}; };
/// This is nn::settings::system::NotificationVolume /// This is nn::settings::system::NotificationVolume
@ -252,7 +255,7 @@ enum class PlatformRegion : s32 {
Terra = 2, Terra = 2,
}; };
constexpr std::array<LanguageCode, 18> available_language_codes = {{
constexpr std::array<LanguageCode, 20> available_language_codes = {{
LanguageCode::JA, LanguageCode::JA,
LanguageCode::EN_US, LanguageCode::EN_US,
LanguageCode::FR, LanguageCode::FR,
@ -271,9 +274,11 @@ constexpr std::array<LanguageCode, 18> available_language_codes = {{
LanguageCode::ZH_HANS, LanguageCode::ZH_HANS,
LanguageCode::ZH_HANT, LanguageCode::ZH_HANT,
LanguageCode::PT_BR, LanguageCode::PT_BR,
LanguageCode::PL,
LanguageCode::TH
}}; }};
static constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_layout{{
static constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 20> language_to_layout{{
{LanguageCode::JA, KeyboardLayout::Japanese}, {LanguageCode::JA, KeyboardLayout::Japanese},
{LanguageCode::EN_US, KeyboardLayout::EnglishUs}, {LanguageCode::EN_US, KeyboardLayout::EnglishUs},
{LanguageCode::FR, KeyboardLayout::French}, {LanguageCode::FR, KeyboardLayout::French},
@ -292,6 +297,8 @@ static constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> languag
{LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified}, {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
{LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional}, {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
{LanguageCode::PT_BR, KeyboardLayout::Portuguese}, {LanguageCode::PT_BR, KeyboardLayout::Portuguese},
{LanguageCode::PL, KeyboardLayout::EnglishUsInternational},
{LanguageCode::TH, KeyboardLayout::EnglishUsInternational}
}}; }};
/// This is nn::settings::system::AccountNotificationFlag /// This is nn::settings::system::AccountNotificationFlag

1
src/qt_common/config/shared_translation.cpp

@ -651,7 +651,6 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent)
PAIR(Language, ChineseSimplified, tr("Simplified Chinese")), PAIR(Language, ChineseSimplified, tr("Simplified Chinese")),
PAIR(Language, ChineseTraditional, tr("Traditional Chinese (正體中文)")), PAIR(Language, ChineseTraditional, tr("Traditional Chinese (正體中文)")),
PAIR(Language, PortugueseBrazilian, tr("Brazilian Portuguese (português do Brasil)")), PAIR(Language, PortugueseBrazilian, tr("Brazilian Portuguese (português do Brasil)")),
PAIR(Language, Serbian, tr("Serbian (српски)")),
PAIR(Language, Polish, tr("Polish (polska)")), PAIR(Language, Polish, tr("Polish (polska)")),
PAIR(Language, Thai, tr("Thai (แบบไทย)")), PAIR(Language, Thai, tr("Thai (แบบไทย)")),
}}); }});

Loading…
Cancel
Save