Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Po filename customization + multiple po sources #308

Merged
merged 3 commits into from
Nov 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,23 @@ Nuggets translations can be modified at runtime as follows:
}
```

#### PO customization

i18n allows you to change the PO file name to use and use PO files from other sources (when working with multiple projects for example).
To enable this feature, you can set :


```xml
<appSettings>
...
<add key="i18n.LocaleFilename" value="messages" />
<add key="i18n.LocaleOtherFiles" value="external1;external2" /><!-- relative path from the directory of {LocaleFilename}.po-->
...
</appSettings>
```

Note : i18n.LocaleOtherFiles paths are relative to the directory of the file {i18n.LocaleFilename}.po (messages.po by default).

### URL Localization

In keeping with emerging standards for internationalized web applications, i18n provides support for
Expand Down
174 changes: 95 additions & 79 deletions src/i18n.Domain/Concrete/POTranslationRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public CacheDependency GetCacheDependencyForAllLanguages()
/// <param name="translation">The translation you wish to save. Must have Language shortag filled out.</param>
public void SaveTranslation(Translation translation)
{
var templateFilePath = GetAbsoluteLocaleDir() + "/messages.pot";
var templateFilePath = GetAbsoluteLocaleDir() + "/" + _settings.LocaleFilename + ".pot";
var POTDate = DateTime.Now;
if (File.Exists(templateFilePath)) {
POTDate = File.GetLastWriteTime(templateFilePath); }
Expand Down Expand Up @@ -258,7 +258,7 @@ public void SaveTranslation(Translation translation)
/// <param name="items">A list of template items to save. The list should be all template items for the entire project.</param>
public void SaveTemplate(IDictionary<string, TemplateItem> items)
{
string filePath = GetAbsoluteLocaleDir() + "/messages.pot";
string filePath = GetAbsoluteLocaleDir() + "/" + _settings.LocaleFilename + ".pot";
string backupPath = filePath + ".backup";

if (File.Exists(filePath)) //we backup one version. more advanced backup solutions could be added here.
Expand Down Expand Up @@ -350,9 +350,11 @@ private string GetAbsoluteLocaleDir()
return _settings.LocaleDirectory;
}

private string GetPathForLanguage(string langtag)
private string GetPathForLanguage(string langtag, string filename = null)
{
return Path.Combine(GetAbsoluteLocaleDir(), langtag, "messages.po");
if (!filename.IsSet())
filename = _settings.LocaleFilename;
return Path.Combine(GetAbsoluteLocaleDir(), langtag, filename + ".po");
}

/// <summary>
Expand All @@ -370,83 +372,97 @@ private Translation ParseTranslationFile(string langtag)
translation.LanguageInformation = language;
var items = new ConcurrentDictionary<string, TranslationItem>();

string path = GetPathForLanguage(langtag);

if (File.Exists(path)) {
DebugHelpers.WriteLine("Reading file: {0}", path);

using (var fs = File.OpenText(path))
{
// http://www.gnu.org/s/hello/manual/gettext/PO-Files.html

string line;
bool itemStarted = false;
while ((line = fs.ReadLine()) != null)
{
var extractedComments = new List<string>();
var translatorComments = new List<string>();
var flags = new List<string>();
var references = new List<ReferenceContext>();

//read all comments, flags and other descriptive items for this string
//if we have #~ its a historical/log entry but it is the messageID/message so we skip this do/while
if (line.StartsWith("#") && !line.StartsWith("#~"))
{
do
{
itemStarted = true;
switch (line[1])
{
case '.': //Extracted comments
extractedComments.Add(line.Substring(2).Trim());
break;
case ':': //references
references.Add(ReferenceContext.Parse(line.Substring(2).Trim()));
break;
case ',': //flags
flags.Add(line.Substring(2).Trim());
break;
case '|': //msgid previous-untranslated-string - NOT used by us
break;
default: //translator comments
translatorComments.Add(line.Substring(1).Trim());
break;
}

} while ((line = fs.ReadLine()) != null && line.StartsWith("#"));
}

if (line != null && (itemStarted || line.StartsWith("#~")))
{
TranslationItem item = ParseBody(fs, line, extractedComments);
if (item != null) {
//
item.TranslatorComments = translatorComments;
item.ExtractedComments = extractedComments;
item.Flags = flags;
item.References = references;
//
items.AddOrUpdate(
item.MsgKey,
// Add routine.
k => {
return item;
},
// Update routine.
(k, v) => {
v.References = v.References.Append(item.References);
var referencesAsComments = item.References.Select(r => r.ToComment()).ToList();
v.ExtractedComments = v.ExtractedComments.Append(referencesAsComments);
v.TranslatorComments = v.TranslatorComments.Append(referencesAsComments);
v.Flags = v.Flags.Append(referencesAsComments);
return v;
});
List<string> paths = new List<string>();

paths.Add(GetPathForLanguage(langtag));

foreach (var file in _settings.LocaleOtherFiles)
{
paths.Add(GetPathForLanguage(langtag, file));
}

foreach (var path in paths)
{
if (File.Exists(path))
{
DebugHelpers.WriteLine("Reading file: {0}", path);

using (var fs = File.OpenText(path))
{
// http://www.gnu.org/s/hello/manual/gettext/PO-Files.html

string line;
bool itemStarted = false;
while ((line = fs.ReadLine()) != null)
{
var extractedComments = new List<string>();
var translatorComments = new List<string>();
var flags = new List<string>();
var references = new List<ReferenceContext>();

//read all comments, flags and other descriptive items for this string
//if we have #~ its a historical/log entry but it is the messageID/message so we skip this do/while
if (line.StartsWith("#") && !line.StartsWith("#~"))
{
do
{
itemStarted = true;
switch (line[1])
{
case '.': //Extracted comments
extractedComments.Add(line.Substring(2).Trim());
break;
case ':': //references
references.Add(ReferenceContext.Parse(line.Substring(2).Trim()));
break;
case ',': //flags
flags.Add(line.Substring(2).Trim());
break;
case '|': //msgid previous-untranslated-string - NOT used by us
break;
default: //translator comments
translatorComments.Add(line.Substring(1).Trim());
break;
}

} while ((line = fs.ReadLine()) != null && line.StartsWith("#"));
}

if (line != null && (itemStarted || line.StartsWith("#~")))
{
TranslationItem item = ParseBody(fs, line, extractedComments);
if (item != null)
{
//
item.TranslatorComments = translatorComments;
item.ExtractedComments = extractedComments;
item.Flags = flags;
item.References = references;
//
items.AddOrUpdate(
item.MsgKey,
// Add routine.
k => {
return item;
},
// Update routine.
(k, v) =>
{
v.References = v.References.Append(item.References);
var referencesAsComments =
item.References.Select(r => r.ToComment()).ToList();
v.ExtractedComments = v.ExtractedComments.Append(referencesAsComments);
v.TranslatorComments = v.TranslatorComments.Append(referencesAsComments);
v.Flags = v.Flags.Append(referencesAsComments);
return v;
});
}
}
}

itemStarted = false;
}
}
itemStarted = false;
}
}
}
}
translation.Items = items;
return translation;
Expand Down
45 changes: 45 additions & 0 deletions src/i18n.Domain/Concrete/i18nSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,51 @@ public virtual string LocaleDirectory

#endregion

#region Locale filename

private const string _localeFilenameDefault = "messages";
public virtual string LocaleFilename
{
get
{
string prefixedString = GetPrefixedString("LocaleFilename");
string setting = _settingService.GetSetting(prefixedString);
if (setting.IsSet())
{
return setting;
}

return _localeFilenameDefault;
}
set
{
string prefixedString = GetPrefixedString("LocaleFilename");
_settingService.SetSetting(prefixedString, value);
}
}

private const string _localeOtherFilesDefault = "";
public virtual IEnumerable<string> LocaleOtherFiles
{
get
{
string prefixedString = GetPrefixedString("LocaleOtherFiles");
string setting = _settingService.GetSetting(prefixedString);
if (!setting.IsSet())
{
setting = _localeOtherFilesDefault;
}

return setting.Split(';');
}
set
{
string prefixedString = GetPrefixedString("LocaleOtherFiles");
_settingService.SetSetting(prefixedString, string.Join(";", value));
}
}

#endregion

#region White list

Expand Down