| 105 | } |
| 106 | |
| 107 | QList<LanguageEntry> discoverAvailableLanguages(const QString& savedOverrideCode) { |
| 108 | QSet<QString> codes; |
| 109 | QList<LanguageEntry> entries; |
| 110 | |
| 111 | // English is always available — it's the source language, no .qm needed. |
| 112 | entries.append({QStringLiteral("en"), buildDisplayName(QStringLiteral("en")), true}); |
| 113 | codes.insert(QStringLiteral("en")); |
| 114 | |
| 115 | static const QRegularExpression qmRegex( |
| 116 | QStringLiteral("^app_([A-Za-z]{2,3}(?:_[A-Za-z]{2,4})?)\\.qm$")); |
| 117 | |
| 118 | for (const QString& path : translationSearchPaths()) { |
| 119 | if (path.isEmpty()) continue; |
| 120 | QDir dir(path); |
| 121 | if (!dir.exists()) continue; |
| 122 | const QFileInfoList files = dir.entryInfoList( |
| 123 | {QStringLiteral("app_*.qm")}, QDir::Files); |
| 124 | for (const QFileInfo& fi : files) { |
| 125 | const auto m = qmRegex.match(fi.fileName()); |
| 126 | if (!m.hasMatch()) continue; |
| 127 | QString raw = m.captured(1); |
| 128 | QString code = raw.section('_', 0, 0).toLower(); // "zh_CN" -> "zh" |
| 129 | if (codes.contains(code)) continue; |
| 130 | codes.insert(code); |
| 131 | entries.append({code, buildDisplayName(code), true}); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | // Sort: English first, rest alphabetical by display name (locale-aware). |
| 136 | if (entries.size() > 1) { |
| 137 | std::sort(entries.begin() + 1, entries.end(), |
| 138 | [](const LanguageEntry& a, const LanguageEntry& b) { |
| 139 | return a.displayName.localeAwareCompare(b.displayName) < 0; |
| 140 | }); |
| 141 | } |
| 142 | |
| 143 | // If the saved override points at a language we didn't find, surface it |
| 144 | // anyway marked "(not installed)" so the user sees their saved choice. |
| 145 | if (!savedOverrideCode.isEmpty() && !codes.contains(savedOverrideCode)) { |
| 146 | entries.append({ |
| 147 | savedOverrideCode, |
| 148 | QStringLiteral("%1 (not installed)").arg(buildDisplayName(savedOverrideCode)), |
| 149 | false |
| 150 | }); |
| 151 | } |
| 152 | return entries; |
| 153 | } |
| 154 | |
| 155 | } // namespace |
| 156 |
no test coverage detected