Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
CheckLocalizationPlugin.c
Go to the documentation of this file.
1[WorkbenchPluginAttribute(name: PLUGIN_NAME, description: "Find non-localized text in UI", wbModules: { "LocalizationEditor" }, shortcut: "Ctrl+Shift+A")]
2class CheckLocalizationPlugin: LocalizationEditorPlugin
3{
4 [Attribute(defvalue: "0", desc: "Add non-localized texts to the currently opened file")]
6
7 [Attribute(defvalue: "", desc: "To which field must the non-localized text be set - e.g m_sSource, Target_en_us, etc")]
8 protected string m_sSourceField;
9
10 [Attribute(defvalue: "ENF-", desc: "Id field prefix for auto-generated ids")]
11 protected string m_sIdPrefix;
12
13 protected static const string PLUGIN_NAME = "Check localization in UI";
14 protected static const int LAYOUT_WARNING_THRESHOLD = 100;
15 protected static const int MAX_DISPLAYED_TEXT_LENGTH = 50;
16 protected static const int MAX_DISPLAYED_TEXTS = 6;
17
18 protected static const string ID_FIELD = "Id";
19 protected static const string ID_WHITELIST = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
20 protected static const int ID_MAX_LENGTH = 12;
21
22 //------------------------------------------------------------------------------------------------
23 override event void Run()
24 {
25 m_sSourceField.TrimInPlace();
26 m_sIdPrefix.TrimInPlace();
27
28 LocalizationEditor localizationEditor = Workbench.GetModule(LocalizationEditor);
29 if (!localizationEditor)
30 return;
31
33 {
34 Workbench.Dialog(PLUGIN_NAME, "The Source Field is empty - please provide it if you intend to add it to the localization file");
35 Configure();
36 return;
37 }
38
39 array<ResourceName> resourceNames = {};
41 filter.fileExtensions = { "layout" };
42 ResourceDatabase.SearchResources(filter, resourceNames.Insert);
43
44 int count = resourceNames.Count();
45 if (count > LAYOUT_WARNING_THRESHOLD) // don't ask for a few files
46 {
47 if (Workbench.ScriptDialog(
49 "You are about to scan " + count + " layout files. Continue?",
50 new WorkbenchDialog_OKCancel()) == 0)
51 return;
52 }
53
54 array<ref CheckLocalizationPluginReport> reports = GetNonLocalizedReports(resourceNames);
55
56 int reportsCount = reports.Count();
57 if (reportsCount < 1)
58 {
59 Workbench.Dialog(PLUGIN_NAME, "All texts in UI are localized.");
60 return;
61 }
62
63 array<string> translationsToAdd;
65 translationsToAdd = {};
66
67 string reportText;
68 int missingCount;
69 foreach (int i, CheckLocalizationPluginReport report : reports)
70 {
71 if (i == 0)
72 reportText += report.m_sResourceName;
73 else
74 reportText += "\n\n" + report.m_sResourceName;
75
76 foreach (CheckLocalizationPluginReportEntry reportEntry : report.m_aEntries)
77 {
78 reportText += string.Format("\n \"%2\" at %1", reportEntry.m_sPath, reportEntry.m_sText);
80 translationsToAdd.Insert(reportEntry.m_sText);
81
82 ++missingCount;
83 }
84 }
85
86 Workbench.Dialog(PLUGIN_NAME, string.Format("Found %1 layouts with %2 non-localized texts!", reportsCount, missingCount), reportText);
88 return;
89
90 if (!localizationEditor.GetTable()) // no table
91 return;
92
93 string message = "You are about to add the following " + missingCount + " non-localized texts to the translation table.";
94 foreach (int i, string translationToAdd : translationsToAdd)
95 {
96 // will replace e.g the last wanted element by (...) in order to keep the good number of lines
97 if (i >= MAX_DISPLAYED_TEXTS - 1 && missingCount > MAX_DISPLAYED_TEXTS)
98 {
99 message += "\n(...)";
100 break;
101 }
102
103 translationToAdd.Replace("\n", "\\n");
104 int length = translationToAdd.Length();
105 if (length > MAX_DISPLAYED_TEXT_LENGTH)
106 translationToAdd = translationToAdd.Substring(0, MAX_DISPLAYED_TEXT_LENGTH - 5) + "(...)"; // hardcoded ellipsis
107
108 message += "\n- " + translationToAdd;
109 }
110
111 message += "\n\nContinue?";
112
113 if (Workbench.ScriptDialog(PLUGIN_NAME, message, new WorkbenchDialog_OKCancel()) == 0)
114 return;
115
116 AddTranslationsToTable(localizationEditor, translationsToAdd);
117 }
118
119 //------------------------------------------------------------------------------------------------
122 protected array<ref CheckLocalizationPluginReport> GetNonLocalizedReports(notnull array<ResourceName> resourceNames)
123 {
124 int count = resourceNames.Count();
125 if (count < 1)
126 return {};
127
128 array<ref CheckLocalizationPluginReport> result = {};
129
130 LocalizationEditor localisationEditor = Workbench.GetModule(LocalizationEditor);
131 float prevProgress, currProgress;
132 WBProgressDialog progress = new WBProgressDialog("Scanning Layouts", localisationEditor);
133 foreach (int i, ResourceName resourceName : resourceNames)
134 {
135 currProgress = (i + 1) / count;
136 if (currProgress - prevProgress >= 0.01) // min 1%
137 {
138 progress.SetProgress(currProgress); // expensive
139 prevProgress = currProgress;
140 }
141
142 Resource resource = BaseContainerTools.LoadContainer(resourceName);
143 if (!resource || !resource.IsValid())
144 continue;
145
146 WidgetSource widgetSource = WidgetSource.Cast(resource.GetResource());
147 if (!widgetSource)
148 continue;
149
150 array<ref CheckLocalizationPluginReportEntry> reportEntries = GetNonLocalizedReportEntries(widgetSource);
151 int entriesCount = reportEntries.Count();
152 if (entriesCount < 1)
153 continue;
154
155 CheckLocalizationPluginReport report = new CheckLocalizationPluginReport();
156 report.m_sResourceName = resourceName;
157 report.m_aEntries = {};
158 report.m_aEntries.Reserve(entriesCount);
159 foreach (CheckLocalizationPluginReportEntry reportEntry : reportEntries)
160 {
161 report.m_aEntries.Insert(reportEntry);
162 }
163
164 result.Insert(report);
165 }
166
167 return result;
168 }
169
170 //------------------------------------------------------------------------------------------------
175 protected array<ref CheckLocalizationPluginReportEntry> GetNonLocalizedReportEntries(notnull WidgetSource widgetSource, string path = string.Empty, int index = -1)
176 {
177 array<ref CheckLocalizationPluginReportEntry> result = {};
178 string widgetName = widgetSource.GetName();
179 if (widgetName.IsEmpty())
180 {
181 if (index > -1)
182 widgetName = string.Format("%1[%2]", widgetSource.GetClassName(), index);
183 else
184 widgetName = string.Format("%1[?]", widgetSource.GetClassName());
185 }
186 else
187 {
188 if (widgetName.Contains("/")) // root widget
189 widgetName = ".";
190 }
191
192 if (path.IsEmpty())
193 path = widgetName;
194 else
195 path = path + "/" + widgetName;
196
197 string text;
198 if (widgetSource.Get("Text", text) && !text.IsEmpty() && !text.Contains("#"))
199 {
201 report.m_sPath = path;
202 report.m_sText = text;
203 result.Insert(report);
204 }
205
206 for (int i, count = widgetSource.GetNumChildren(); i < count; i++)
207 {
208 WidgetSource childSource = widgetSource.GetChild(i);
209 if (!childSource)
210 continue;
211
212 array<ref CheckLocalizationPluginReportEntry> reportEntries = GetNonLocalizedReportEntries(childSource, path, i);
213 foreach (CheckLocalizationPluginReportEntry reportEntry : reportEntries)
214 {
215 result.Insert(reportEntry);
216 }
217 }
218
219 return result;
220 }
221
222 //------------------------------------------------------------------------------------------------
225 protected void AddTranslationsToTable(notnull LocalizationEditor localizationEditor, notnull array<string> sourceTexts)
226 {
227 if (sourceTexts.IsEmpty())
228 return;
229
230 BaseContainerList items = localizationEditor.GetTable().GetObjectArray("Items");
231
232 array<string> existingIDs = {};
233 for (int i, itemsCount = items.Count(); i < itemsCount; ++i)
234 {
235 string id;
236 if (items.Get(i).Get(ID_FIELD, id))
237 existingIDs.Insert(id);
238 }
239
240 localizationEditor.BeginModify(PLUGIN_NAME);
241 int sourceIndex = -1;
242 foreach (string sourceText : sourceTexts)
243 {
244 string id = GetTextId(sourceText);
245 bool hasID = existingIDs.Contains(id);
246 if (hasID)
247 {
248 for (int i; i < 1000; ++i)
249 {
250 id = GetTextId(sourceText, i);
251 hasID = existingIDs.Contains(id);
252 if (!hasID)
253 break;
254 }
255 }
256
257 if (hasID)
258 {
259 Workbench.Dialog(PLUGIN_NAME, "Failed on " + id);
260 break;
261 }
262
263 BaseContainer item = localizationEditor.InsertItem(id, true, true);
264 if (sourceIndex < 0)
265 {
266 sourceIndex = item.GetVarIndex(m_sSourceField);
267 if (sourceIndex < 0)
268 {
269 localizationEditor.DeleteItem(id);
270 Workbench.Dialog(PLUGIN_NAME, "Cannot find source field \"" + m_sSourceField + "\"");
271 break;
272 }
273 }
274
275 localizationEditor.ModifyProperty(item, sourceIndex, sourceText);
276 }
277
278 localizationEditor.EndModify();
279 }
280
281 //------------------------------------------------------------------------------------------------
285 protected string GetTextId(string text, int attemptNumber = -1)
286 {
287 bool afterSpace = true;
288 string filteredAndCased;
289 for (int i, length = text.Length(); i < length; ++i)
290 {
291 string character = text[i];
292 if (character == " " || character == "\t" || character == "\n")
293 {
294 afterSpace = true;
295 }
296 else
297 if (ID_WHITELIST.Contains(character))
298 {
299 if (afterSpace)
300 {
301 character.ToUpper();
302 afterSpace = false;
303 }
304
305 filteredAndCased += character;
306 }
307 }
308
309 if (filteredAndCased.IsEmpty())
310 {
311 if (attemptNumber < 0)
312 return m_sIdPrefix + "Rename";
313 else
314 return m_sIdPrefix + "Rename_" + attemptNumber.ToString(3);
315 }
316 else
317 {
318 if (attemptNumber < 0)
319 return m_sIdPrefix + filteredAndCased;
320 else
321 return m_sIdPrefix + filteredAndCased + attemptNumber.ToString(3);
322 }
323 }
324
325 //------------------------------------------------------------------------------------------------
326 override protected event void Configure()
327 {
328 Workbench.ScriptDialog(PLUGIN_NAME, string.Empty, this);
329 }
330
331 //------------------------------------------------------------------------------------------------
332 [ButtonAttribute("Close")]
333 protected int ButtonClose()
334 {
335 return 0;
336 }
337}
338
339class CheckLocalizationPluginReport
340{
342 ref array<ref CheckLocalizationPluginReportEntry> m_aEntries;
343}
344
346{
347 string m_sPath;
348 string m_sText;
349}
string path
AddonBuildInfoTool id
GenerateFlowMaps WorkbenchPlugin WorkbenchPluginAttribute("Regenerate river flow-maps", "Generate and save/overwrite river flow-maps", "", "", {"WorldEditor"}, "", 0xf773)
Definition FlowmapTool.c:59
ResourceName resourceName
Definition SCR_AIGroup.c:66
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
override void Configure()
class WorkbenchDialog_AbortRetryIgnore ButtonAttribute("OK", true)
static const int MAX_DISPLAYED_TEXT_LENGTH
cuts the translation line at 50 chars (45 + ellipsis)
array< ref CheckLocalizationPluginReport > GetNonLocalizedReports(notnull array< ResourceName > resourceNames)
array< ref CheckLocalizationPluginReportEntry > GetNonLocalizedReportEntries(notnull WidgetSource widgetSource, string path=string.Empty, int index=-1)
void AddTranslationsToTable(notnull LocalizationEditor localizationEditor, notnull array< string > sourceTexts)
string GetTextId(string text, int attemptNumber=-1)
static const int MAX_DISPLAYED_TEXTS
max item ids displayed in the "are you sure?" modal
static const int LAYOUT_WARNING_THRESHOLD
rings at 100 and above
Object holding reference to resource. In destructor release the resource.
Definition Resource.c:25
Object used for holding filtering params for ResourceDatabase.SearchResources() method.
Definition System.c:9
SCR_FieldOfViewSettings Attribute