Audacity  3.0.3
Internat.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  Internat.cpp
6 
7  Markus Meyer
8  Dominic Mazzoni (Mac OS X code)
9 
10 *******************************************************************//*******************************************************************/
22 
23 #include "Internat.h"
24 
25 
26 #include "MemoryX.h"
27 
28 #include <wx/log.h>
29 #include <wx/intl.h>
30 #include <wx/filename.h>
31 
32 #include <locale.h>
33 #include <math.h> // for pow()
34 
35 #include "../include/audacity/ComponentInterface.h"
36 
37 // in order for the static member variables to exist, they must appear here
38 // (_outside_) the class definition, in order to be allocated some storage.
39 // Otherwise, you get link errors.
40 
41 wxChar Internat::mDecimalSeparator = wxT('.'); // default
42 // exclude is used by SanitiseFilename.
43 wxArrayString Internat::exclude;
44 
45 // DA: Use tweaked translation mechanism to replace 'Audacity' by 'DarkAudacity'.
46 #ifdef EXPERIMENTAL_DA
47 // This function allows us to replace Audacity by DarkAudacity without peppering
48 // the source code with changes. We split out this step, the customisation, as
49 // it is used on its own (without translation) in the wxTS macro.
50 AUDACITY_DLL_API const wxString& GetCustomSubstitution(const wxString& str2)
51 {
52  // If contains 'DarkAudacity, already converted.
53  if( str2.Contains( "DarkAudacity" ))
54  return str2;
55  // If does not contain 'Audacity', nothing to do.
56  if( !str2.Contains( "Audacity" ))
57  return str2;
58  wxString str3 = str2;
59  str3.Replace( "Audacity", "DarkAudacity" );
60  str3.Replace( " an DarkAudacity", " a DarkAudacity" );
61  // DA also renames sync-lock(ed) as time-lock(ed).
62  str3.Replace( "Sync-Lock", "Time-Lock" );
63  str3.Replace( "Sync-&Lock", "Time-&Lock" );
64  str3.Replace( "Sync Lock", "Time Lock" );
65  return wxTranslations::GetUntranslatedString(str3);
66 }
67 #else
68 AUDACITY_DLL_API const wxString& GetCustomSubstitution(const wxString& str1)
69 {
70  return str1 ;
71 }
72 #endif
73 
74 // In any translated string, we can replace the name 'Audacity' by 'DarkAudacity'
75 // without requiring translators to see extra strings for the two versions.
76 AUDACITY_DLL_API const wxString& GetCustomTranslation(const wxString& str1)
77 {
78  const wxString& str2 = wxGetTranslation( str1 );
79  return GetCustomSubstitution( str2 );
80 }
81 
82 
84 {
85  // Save decimal point character
86  struct lconv * localeInfo = localeconv();
87  if (localeInfo)
88  mDecimalSeparator = wxString(wxSafeConvertMB2WX(localeInfo->decimal_point)).GetChar(0);
89 
90 // wxLogDebug(wxT("Decimal separator set to '%c'"), mDecimalSeparator);
91 
92  // Setup list of characters that aren't allowed in file names
93  // Hey! The default wxPATH_NATIVE does not do as it should.
94 #if defined(__WXMAC__)
95  wxPathFormat format = wxPATH_MAC;
96 #elif defined(__WXGTK__)
97  wxPathFormat format = wxPATH_UNIX;
98 #elif defined(__WXMSW__)
99  wxPathFormat format = wxPATH_WIN;
100 #endif
101 
102  // This is supposed to return characters not permitted in paths to files
103  // or to directories
104  auto forbid = wxFileName::GetForbiddenChars(format);
105 
106  for (auto cc: forbid) {
107 #if defined(__WXGTK__)
108  if (cc == wxT('*') || cc == wxT('?')) {
109  continue;
110  }
111 #endif
112  exclude.push_back(wxString{ cc });
113  }
114 
115  // The path separators may not be forbidden, so add them
116  //auto separators = wxFileName::GetPathSeparators(format);
117 
118  // Bug 1441 exclude all separators from filenames on all platforms.
119  auto separators = wxString("\\/");
120 
121  for(auto cc: separators) {
122  if (forbid.Find(cc) == wxNOT_FOUND)
123  exclude.push_back(wxString{ cc });
124  }
125 }
126 
128 {
129  wxSetlocale( LC_NUMERIC, "C" );
130  mDecimalSeparator = '.';
131 }
132 
133 
135 {
136  return mDecimalSeparator;
137 }
138 
139 bool Internat::CompatibleToDouble(const wxString& stringToConvert, double* result)
140 {
141  // Regardless of the locale, always respect comma _and_ point
142  wxString s = stringToConvert;
143  s.Replace(wxT(","), wxString(GetDecimalSeparator()));
144  s.Replace(wxT("."), wxString(GetDecimalSeparator()));
145  return s.ToDouble(result);
146 }
147 
148 double Internat::CompatibleToDouble(const wxString& stringToConvert)
149 {
150  double result = 0;
151  Internat::CompatibleToDouble(stringToConvert, &result);
152  return result;
153 }
154 
155 wxString Internat::ToString(double numberToConvert,
156  int digitsAfterDecimalPoint /* = -1 */)
157 {
158  wxString result = ToDisplayString(
159  numberToConvert, digitsAfterDecimalPoint);
160 
161  result.Replace(wxString(GetDecimalSeparator()), wxT("."));
162 
163  return result;
164 }
165 
166 wxString Internat::ToDisplayString(double numberToConvert,
167  int digitsAfterDecimalPoint /* = -1 */)
168 {
169  wxString decSep = wxString(GetDecimalSeparator());
170  wxString result;
171 
172  if (digitsAfterDecimalPoint == -1)
173  {
174  result.Printf(wxT("%f"), numberToConvert);
175 
176  // Not all libcs respect the decimal separator, so always convert
177  // any dots found to the decimal separator.
178  result.Replace(wxT("."), decSep);
179 
180  if (result.Find(decSep) != -1)
181  {
182  // Strip trailing zeros, but leave one, and decimal separator.
183  int pos = result.length() - 1;
184  while ((pos > 1) &&
185  (result.GetChar(pos) == wxT('0')) &&
186  (result.GetChar(pos - 1) != decSep))
187  pos--;
188  // (Previous code removed all of them and decimal separator.)
189  // if (result.GetChar(pos) == decSep)
190  // pos--; // strip point before empty fractional part
191  result = result.Left(pos+1);
192  }
193  }
194  else
195  {
196  wxString format;
197  format.Printf(wxT("%%.%if"), digitsAfterDecimalPoint);
198  result.Printf(format, numberToConvert);
199 
200  // Not all libcs respect the decimal separator, so always convert
201  // any dots found to the decimal separator
202  result.Replace(wxT("."), decSep);
203  }
204 
205  return result;
206 }
207 
209 {
210  /* wxLongLong contains no built-in conversion to double */
211  double dSize = size.GetHi() * pow(2.0, 32); // 2 ^ 32
212  dSize += size.GetLo();
213 
214  return FormatSize(dSize);
215 }
216 
218 {
219  TranslatableString sizeStr;
220 
221  if (size == -1)
222  sizeStr = XO("Unable to determine");
223  else {
224  /* make it look nice, by formatting into k, MB, etc */
225  if (size < 1024.0)
226  sizeStr = XO("%s bytes").Format( ToDisplayString(size) );
227  else if (size < 1024.0 * 1024.0) {
228  /* i18n-hint: Abbreviation for Kilo bytes */
229  sizeStr = XO("%s KB").Format( ToDisplayString(size / 1024.0, 1) );
230  }
231  else if (size < 1024.0 * 1024.0 * 1024.0) {
232  /* i18n-hint: Abbreviation for Mega bytes */
233  sizeStr = XO("%s MB").Format( ToDisplayString(size / (1024.0 * 1024.0), 1) );
234  }
235  else {
236  /* i18n-hint: Abbreviation for Giga bytes */
237  sizeStr = XO("%s GB").Format( ToDisplayString(size / (1024.0 * 1024.0 * 1024.0), 1) );
238  }
239  }
240 
241  return sizeStr;
242 }
243 
244 bool Internat::SanitiseFilename(wxString &name, const wxString &sub)
245 {
246  bool result = false;
247  for(const auto &item : exclude)
248  {
249  if(name.Contains(item))
250  {
251  name.Replace(item, sub);
252  result = true;
253  }
254  }
255 
256 #ifdef __WXMAC__
257  // Special Mac stuff
258  // '/' is permitted in file names as seen in dialogs, even though it is
259  // the path separator. The "real" filename as seen in the terminal has ':'.
260  // Do NOT return true if this is the only substitution.
261  name.Replace(wxT("/"), wxT(":"));
262 #endif
263 
264  return result;
265 }
266 
268  const EnumValueSymbol strings[], size_t nStrings)
269 {
270  return transform_range<TranslatableStrings>(
271  strings, strings + nStrings,
272  std::mem_fn( &EnumValueSymbol::Msgid )
273  );
274 }
275 
276 TranslatableStrings Msgids( const std::vector<EnumValueSymbol> &strings )
277 {
278  return Msgids( strings.data(), strings.size() );
279 }
280 
281 // Find a better place for this?
282 #include "audacity/Types.h"
284  std::initializer_list<Identifier> components, wxChar separator )
285 {
286  if( components.size() < 2 )
287  {
288  wxASSERT( false );
289  return;
290  }
291  auto iter = components.begin(), end = components.end();
292  value = (*iter++).value;
293  while (iter != end)
294  value += separator + (*iter++).value;
295 }
296 
297 std::vector< Identifier > Identifier::split( wxChar separator ) const
298 {
299  auto strings = ::wxSplit( value, separator );
300  return { strings.begin(), strings.end() };
301 }
302 
303 const wxChar *const TranslatableString::NullContextName = wxT("*");
304 
307  [](const wxString & str, TranslatableString::Request request) -> wxString {
308  switch ( request ) {
309  case Request::Context:
310  return NullContextName;
311  case Request::Format:
312  case Request::DebugFormat:
313  default:
314  return str;
315  }
316  }
317 };
318 
320 {
322 }
323 
325 {
326  auto prevFormatter = mFormatter;
327  mFormatter = [prevFormatter, codes]
328  ( const wxString & str, TranslatableString::Request request ) -> wxString {
329  switch ( request ) {
330  case Request::Context:
331  return TranslatableString::DoGetContext( prevFormatter );
332  case Request::Format:
333  case Request::DebugFormat:
334  default: {
335  bool debug = request == Request::DebugFormat;
336  auto result =
338  prevFormatter,
339  str, TranslatableString::DoGetContext( prevFormatter ),
340  debug );
341  if ( codes & MenuCodes )
342  result = wxStripMenuCodes( result );
343  if ( codes & Ellipses ) {
344  if (result.EndsWith(wxT("...")))
345  result = result.Left( result.length() - 3 );
346  // Also check for the single-character Unicode ellipsis
347  else if (result.EndsWith(wxT("\u2026")))
348  result = result.Left( result.length() - 1 );
349  }
350  return result;
351  }
352  }
353  };
354 
355  return *this;
356 }
357 
358 wxString TranslatableString::DoGetContext( const Formatter &formatter )
359 {
360  return formatter ? formatter( {}, Request::Context ) : wxString{};
361 }
362 
363 wxString TranslatableString::DoSubstitute( const Formatter &formatter,
364  const wxString &format, const wxString &context, bool debug )
365 {
366  return formatter
367  ? formatter( format, debug ? Request::DebugFormat : Request::Format )
368  : // come here for most translatable strings, which have no formatting
369  ( debug ? format : wxGetTranslation( format, wxString{}, context ) );
370 }
371 
373  const Formatter &formatter,
374  const wxString &singular, const wxString &plural, unsigned nn, bool debug )
375 {
376  // come here for translatable strings that choose among forms by number;
377  // if not debugging, then two keys are passed to an overload of
378  // wxGetTranslation, and also a number.
379  // Some languages might choose among more or fewer than two forms
380  // (e.g. Arabic has duals and Russian has complicated declension rules)
381  wxString context;
382  return ( debug || NullContextName == (context = DoGetContext(formatter)) )
383  ? ( nn == 1 ? singular : plural )
384  : wxGetTranslation(
385  singular, plural, nn
387  , wxString{} // domain
388  , context
389 #endif
390  );
391 }
392 
394  const TranslatableString arg, const wxString &separator ) &
395 {
396  auto prevFormatter = mFormatter;
397  mFormatter =
398  [prevFormatter,
399  arg /* = std::move( arg ) */,
400  separator](const wxString &str, Request request)
401  -> wxString {
402  switch ( request ) {
403  case Request::Context:
404  return TranslatableString::DoGetContext( prevFormatter );
405  case Request::Format:
406  case Request::DebugFormat:
407  default: {
408  bool debug = request == Request::DebugFormat;
409  return
410  TranslatableString::DoSubstitute( prevFormatter,
411  str, TranslatableString::DoGetContext( prevFormatter ),
412  debug )
413  + separator
414  + arg.DoFormat( debug );
415  }
416  }
417  };
418  return *this;
419 }
420 
TranslatableString::Request::Format
@ Format
TranslatableString
Definition: Types.h:290
TranslatableString::DoGetContext
static wxString DoGetContext(const Formatter &formatter)
Definition: Internat.cpp:358
TranslatableString::IsVerbatim
bool IsVerbatim() const
Definition: Internat.cpp:319
Identifier::Identifier
Identifier()=default
TranslatableString::Request
Request
Definition: Types.h:475
TranslatableString::Inaudible
static const TranslatableString Inaudible
Definition: Types.h:292
Internat::GetDecimalSeparator
static wxChar GetDecimalSeparator()
Get the decimal separator for the current locale.
Definition: Internat.cpp:134
XO
#define XO(s)
Definition: Internat.h:32
GetCustomTranslation
AUDACITY_DLL_API const wxString & GetCustomTranslation(const wxString &str1)
Definition: Internat.cpp:76
TranslatableString::NullContextFormatter
static const Formatter NullContextFormatter
Definition: Types.h:463
ComponentInterfaceSymbol::Msgid
const TranslatableString & Msgid() const
Definition: ComponentInterface.h:89
ComponentInterfaceSymbol
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
Definition: ComponentInterface.h:60
TranslatableString::DoChooseFormat
static wxString DoChooseFormat(const Formatter &formatter, const wxString &singular, const wxString &plural, unsigned nn, bool debug)
Definition: Internat.cpp:372
TranslatableString::Request::Context
@ Context
TranslatableString::Strip
TranslatableString & Strip(unsigned options=MenuCodes) &
Definition: Internat.cpp:324
GetCustomSubstitution
AUDACITY_DLL_API const wxString & GetCustomSubstitution(const wxString &str1)
Definition: Internat.cpp:68
Identifier::split
std::vector< Identifier > split(wxChar separator) const
Definition: Internat.cpp:297
TranslatableStrings
std::vector< TranslatableString > TranslatableStrings
Definition: Types.h:555
TranslatableString::mFormatter
Formatter mFormatter
Definition: Types.h:546
TranslatableString::DoSubstitute
static wxString DoSubstitute(const Formatter &formatter, const wxString &format, const wxString &context, bool debug)
Definition: Internat.cpp:363
Internat::FormatSize
static TranslatableString FormatSize(wxLongLong size)
Convert a number to a string while formatting it in bytes, KB, MB, GB.
Definition: Internat.cpp:208
Msgids
TranslatableStrings Msgids(const EnumValueSymbol strings[], size_t nStrings)
Definition: Internat.cpp:267
name
const TranslatableString name
Definition: Distortion.cpp:98
format
int format
Definition: ExportPCM.cpp:54
Identifier::value
wxString value
Definition: Types.h:115
HAS_I18N_CONTEXTS
#define HAS_I18N_CONTEXTS
Definition: Internat.h:171
TranslatableString::Request::DebugFormat
@ DebugFormat
Internat.h
Types.h
MemoryX.h
Internat::SetCeeNumberFormat
static void SetCeeNumberFormat()
Definition: Internat.cpp:127
Internat::mDecimalSeparator
static wxChar mDecimalSeparator
Definition: Internat.h:154
Internat::ToDisplayString
static wxString ToDisplayString(double numberToConvert, int digitsAfterDecimalPoint=-1)
Convert a number to a string, uses the user's locale's decimal separator.
Definition: Internat.cpp:166
TranslatableString::Join
TranslatableString & Join(TranslatableString arg, const wxString &separator={}) &
Definition: Internat.cpp:393
TranslatableString::Formatter
std::function< wxString(const wxString &, Request) > Formatter
Definition: Types.h:302
Internat::ToString
static wxString ToString(double numberToConvert, int digitsAfterDecimalPoint=-1)
Convert a number to a string, always uses the dot as decimal separator.
Definition: Internat.cpp:155
Internat::SanitiseFilename
static bool SanitiseFilename(wxString &name, const wxString &sub)
Check a proposed file name string for illegal characters and remove them return true iff name is "vis...
Definition: Internat.cpp:244
TranslatableString::NullContextName
static const wxChar *const NullContextName
Definition: Types.h:481
Internat::exclude
static wxArrayString exclude
Definition: Internat.h:156
Internat::CompatibleToDouble
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:139
Internat::Init
static void Init()
Initialize internationalisation support. Call this once at program start.
Definition: Internat.cpp:83