Skip to content
Open
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
145 changes: 125 additions & 20 deletions Vernacular.Catalog/Vernacular/PluralRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
//
// Author:
// Aaron Bockover <abock@rd.io>
// Stephane Delcroix <stephane@delcroix.org>
//
// Copyright 2012 Rdio, Inc.
// Copyright 2012 S. Delcroix
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
Expand All @@ -30,7 +32,29 @@ namespace Vernacular
{
public static class PluralRules
{
struct PluralInfo
{
public int nforms;
public string header;
public Func<int, int> getOrder;
}

public static int GetOrder (string isoLanguageCode, int n)
{
return GetPluralInfo(isoLanguageCode).getOrder(n);
}

public static int GetNumberOfPlurals (string isoLanguageCode)
{
return GetPluralInfo(isoLanguageCode).nforms;
}

public static string GetPluralHeader (string isoLanguageCode)
{
return GetPluralInfo(isoLanguageCode).header;
}

static PluralInfo GetPluralInfo (string isoLanguageCode)
{
switch (isoLanguageCode) {
case "ay": // Aymará
Expand All @@ -56,10 +80,19 @@ public static int GetOrder (string isoLanguageCode, int n)
case "vi": // Vietnamese
case "wo": // Wolof
// 1 form
return 0;
return new PluralInfo
{
nforms = 1,
header = "Plural-Forms: nplurals=1; plural=0;",
getOrder = n => 0
};
case "is": // Icelandic
// 2 forms
return (n % 10 != 1 || n % 100 == 11) ? 1 : 0;
return new PluralInfo {
nforms = 2,
header = "Plural-Forms: nplurals=2; plural=(n % 10 != 1 || n % 100 == 11) ? 1 : 0;",
getOrder = n => (n % 10 != 1 || n % 100 == 11) ? 1 : 0
};
case "af": // Afrikaans
case "an": // Aragonese
case "ast": // Asturian
Expand Down Expand Up @@ -124,13 +157,25 @@ public static int GetOrder (string isoLanguageCode, int n)
case "ur": // Urdu
case "yo": // Yoruba
// 2 forms
return n != 1 ? 1 : 0;
return new PluralInfo {
nforms = 2,
header = "Plural-Forms: nplurals=2; plural=n != 1 ? 1 : 0;",
getOrder = n => n != 1 ? 1 : 0
};
case "mk": // Macedonian
// 2 forms
return n == 1 || n % 10 == 1 ? 0 : 1;
return new PluralInfo {
nforms = 2,
header = "Plural-Forms: nplurals=2; plural=n == 1 || n % 10 == 1 ? 0 : 1;",
getOrder = n => n == 1 || n % 10 == 1 ? 0 : 1
};
case "jv": // Javanese
// 2 forms
return n != 0 ? 1 : 0;
return new PluralInfo {
nforms = 2,
header = "Plural-Forms: nplurals=2; plural=n != 0 ? 1 : 0;",
getOrder = n => n != 0 ? 1 : 0
};
case "ach": // Acholi (maybe)
case "ak": // Akan
case "am": // Amharic
Expand All @@ -150,54 +195,114 @@ public static int GetOrder (string isoLanguageCode, int n)
case "wa": // Walloon
case "zh": // Chinese
// 2 forms
return n > 1 ? 1 : 0;
return new PluralInfo {
nforms = 2,
header = "Plural-Forms: nplurals=2; plural=n > 1 ? 1 : 0;",
getOrder = n => n > 1 ? 1 : 0
};
case "lt": // Lithuanian
// 3 forms
return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
return new PluralInfo {
nforms = 3,
header = "Plural-Forms: nplurals=3; plural=n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;",
getOrder = n => n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2
};
case "cs": // Czech
case "sk": // Slovak
// 3 forms
return n == 1 ? 0 : (n >= 2 && n <= 4) ? 1 : 2;
return new PluralInfo {
nforms = 3,
header = "Plural-Forms: nplurals=3; plural=n == 1 ? 0 : (n >= 2 && n <= 4) ? 1 : 2;",
getOrder = n => n == 1 ? 0 : (n >= 2 && n <= 4) ? 1 : 2
};
case "mnk": // Mandinka
// 3 forms
return n == 0 ? 0 : n == 1 ? 1 : 2;
return new PluralInfo {
nforms = 3,
header = "Plural-Forms: nplurals=3; plural=n == 0 ? 0 : n == 1 ? 1 : 2;",
getOrder = n => n == 0 ? 0 : n == 1 ? 1 : 2
};
case "lv": // Latvian
// 3 forms
return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
return new PluralInfo {
nforms = 3,
header = "Plural-Forms: nplurals=3; plural=n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;",
getOrder = n => n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2
};
case "ro": // Romanian
// 3 forms
return n == 1 ? 0 : (n == 0 || (n % 100 > 0 && n % 100 < 20)) ? 1 : 2;
return new PluralInfo {
nforms = 3,
header = "Plural-Forms: nplurals=3; plural=n == 1 ? 0 : (n == 0 || (n % 100 > 0 && n % 100 < 20)) ? 1 : 2;",
getOrder = n => n == 1 ? 0 : (n == 0 || (n % 100 > 0 && n % 100 < 20)) ? 1 : 2
};
case "be": // Belarusian
case "bs": // Bosnian
case "hr": // Croatian
case "ru": // Russian
case "sr": // Serbian
case "uk": // Ukrainian
// 3 forms
return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
return new PluralInfo {
nforms = 3,
header = "Plural-Forms: nplurals=3; plural=n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;",
getOrder = n => n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2
};
case "pl": // Polish
// 3 forms
return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
return new PluralInfo {
nforms = 3,
header = "Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;",
getOrder = n => n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2
};
case "kw": // Cornish
// 4 forms
return n == 1 ? 0 : (n == 2) ? 1 : (n == 3) ? 2 : 3;
return new PluralInfo {
nforms = 4,
header = "Plural-Forms: nplurals=4; plural=n == 1 ? 0 : (n == 2) ? 1 : (n == 3) ? 2 : 3;",
getOrder = n => n == 1 ? 0 : (n == 2) ? 1 : (n == 3) ? 2 : 3
};
case "gd": // Scottish Gaelic
// 4 forms
return (n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : (n > 2 && n < 20) ? 2 : 3;
return new PluralInfo {
nforms = 4,
header = "Plural-Forms: nplurals=4; plural=(n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : (n > 2 && n < 20) ? 2 : 3;",
getOrder = n => (n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : (n > 2 && n < 20) ? 2 : 3
};
case "mt": // Maltese
// 4 forms
return n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3;
return new PluralInfo {
nforms = 4,
header = "Plural-Forms: nplurals=4; plural=n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3;",
getOrder = n => n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3
};
case "cy": // Welsh
// 4 forms
return n == 1 ? 0 : (n == 2) ? 1 : (n != 8 && n != 11) ? 2 : 3;
return new PluralInfo {
nforms = 4,
header = "Plural-Forms: nplurals=4; plural=n == 1 ? 0 : (n == 2) ? 1 : (n != 8 && n != 11) ? 2 : 3;",
getOrder = n => n == 1 ? 0 : (n == 2) ? 1 : (n != 8 && n != 11) ? 2 : 3
};
case "sl": // Slovenian
// 4 forms
return n % 100 == 1 ? 1 : n % 100 == 2 ? 2 : n % 100 == 3 || n % 100 == 4 ? 3 : 0;
return new PluralInfo {
nforms = 4,
header = "Plural-Forms: nplurals=4; plural=n % 100 == 1 ? 1 : n % 100 == 2 ? 2 : n % 100 == 3 || n % 100 == 4 ? 3 : 0;",
getOrder = n => n % 100 == 1 ? 1 : n % 100 == 2 ? 2 : n % 100 == 3 || n % 100 == 4 ? 3 : 0
};
case "ga": // Irish
// 5 forms
return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
return new PluralInfo {
nforms = 5,
header = "Plural-Forms: nplurals=5; plural=n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;",
getOrder = n => n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4
};
default:
return n != 1 ? 1 : 0;
return new PluralInfo {
nforms = 2,
header = "Plural-Forms: nplurals=2; plural=n != 1 ? 1 : 0;",
getOrder = n => n != 1 ? 1 : 0
};
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Vernacular.Potato/Vernacular.Potato/Document.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;

using Vernacular.Potato.Internal;

Expand Down Expand Up @@ -109,7 +110,6 @@ private IEnumerable<Unit> GetAllUnits ()
public override string Generate ()
{
var builder = new StringBuilder ();

foreach (var part in GetAllUnits ()) {
builder.Append (part.Generate ());
builder.Append ("\n\n");
Expand Down
12 changes: 11 additions & 1 deletion Vernacular.Tool/Vernacular.Generators/PoGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public sealed class PoGenerator : Generator
public bool PotMode { get; set; }
public bool ExcludeHeaderMetadata { get; set; }

public string InitWithLocale { get; set; }

protected override void Generate ()
{
var document = new Document ();
Expand All @@ -53,6 +55,9 @@ protected override void Generate ()

document.Headers.PopulateWithRequiredHeaders ();
document.Headers ["X-Generator"] = "Vernacular";
if (!String.IsNullOrEmpty(InitWithLocale)) {
document.Headers["Plural-Forms"] = PluralRules.GetPluralHeader(InitWithLocale);
}
}

var sorted_strings = from localized_string in Strings
Expand Down Expand Up @@ -131,6 +136,11 @@ orderby localized_string.UntranslatedPluralValue
);

var translated = localized_string.TranslatedValues;

if (!string.IsNullOrEmpty(InitWithLocale) && plural != null) {
translated = new string[PluralRules.GetNumberOfPlurals (InitWithLocale)];
}

if (translated == null) {
if (plural == null) {
translated = new [] { singular };
Expand All @@ -143,7 +153,7 @@ orderby localized_string.UntranslatedPluralValue
unit.Add (new Message {
Type = translated.Length == 1 ? MessageType.SingularString : MessageType.PluralString,
PluralOrder = i,
Value = PotMode ? String.Empty : translated [i]
Value = (PotMode || translated[i] == null) ? String.Empty : translated [i]
});
}

Expand Down
14 changes: 14 additions & 0 deletions Vernacular.Tool/Vernacular.Tool/Entry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public static int Main (string[] args)
string android_input_strings_xml = null;
string android_output_strings_xml = null;
string analyer_config_path = null;
string initWithLocale = null;
LocalizationMetadata metadata = null;
bool generate_pot = false;
bool exclude_po_header = false;
Expand Down Expand Up @@ -80,6 +81,7 @@ public static int Main (string[] args)
"for preserving hand-maintained string resources", v => android_input_strings_xml = v },
{ "android-output-strings-xml=", "Output file of localized Android Strings.xml " +
"for preserving hand-maintained string resources", v => android_output_strings_xml = v },
{ "init-with-locale=", "Init a .po file for the locale", v => initWithLocale = v},
{ "pot", v => generate_pot = v != null },
{ "exclude-po-header", v => exclude_po_header = v != null },
{ "l|log", "Display logging", v => log = v != null },
Expand Down Expand Up @@ -128,6 +130,18 @@ public static int Main (string[] args)
((PoGenerator)generator).ExcludeHeaderMetadata = exclude_po_header;
}

if (initWithLocale != null && !(generator is PoGenerator)) {
throw new OptionException ("init-with-locale option only valid with po generator", "init-with-locale");
}

if (initWithLocale != null && generate_pot) {
throw new OptionException ("you can not use init-with-locale with -pot", "init-with-locale");
}

if (initWithLocale != null) {
(generator as PoGenerator).InitWithLocale = initWithLocale;
}

if (reduce_master_path != null && reduce_retain_path == null) {
throw new OptionException ("reduce-retain must be specified if reduce-master is", "reduce-retain");
} else if (reduce_master_path == null && reduce_retain_path != null) {
Expand Down