1
/* Copyright 2016 Software Freedom Conservancy Inc.
3
* This software is licensed under the GNU Lesser General Public License
4
* (version 2.1 or later). See the COPYING file in this distribution.
7
extern const string LANGUAGE_SUPPORT_DIRECTORY;
8
extern const string ISO_CODE_639_XML;
9
extern const string ISO_CODE_3166_XML;
10
public const string TRANSLATABLE = "translatable";
12
namespace International {
14
private GLib.HashTable<string, string> language_names = null;
15
private GLib.HashTable<string, string> country_names = null;
17
public const string SYSTEM_LOCALE = "";
19
void init(string package_name, string program_path, string locale = SYSTEM_LOCALE) {
20
Intl.setlocale(LocaleCategory.ALL, locale);
21
Intl.bindtextdomain(package_name, get_langpack_dir_path(program_path));
22
Intl.bind_textdomain_codeset(package_name, "UTF-8");
23
Intl.textdomain(package_name);
26
// TODO: Geary should be able to use langpacks from the build directory
27
private string get_langpack_dir_path(string program_path) {
28
return LANGUAGE_SUPPORT_DIRECTORY;
31
public string[] get_available_dictionaries() {
32
string[] dictionaries = {};
34
Enchant.Broker broker = new Enchant.Broker();
35
broker.list_dicts((lang_tag, provider_name, provider_desc, provider_file) => {
36
dictionaries += lang_tag;
39
// Whenever regional variants of the dictionaries are available use them
40
// in place of the generic ones, e.g., discard en if en_US, en_GB, ...
41
// are installed on the system.
42
GLib.GenericSet<string> regional_dictionaries =
43
new GLib.GenericSet<string>(GLib.str_hash, GLib.str_equal);
44
foreach (string dic in dictionaries) {
46
int underscore = dic.index_of_char('_');
47
regional_dictionaries.add(dic.substring(0, underscore));
51
GLib.List<string> filtered_dictionaries = new GLib.List<string>();
52
foreach (string dic in dictionaries) {
53
if ("_" in dic || ! regional_dictionaries.contains(dic))
54
filtered_dictionaries.append(dic);
57
filtered_dictionaries.sort((dic_a, dic_b) => (dic_a < dic_b) ? -1 : 1);
60
foreach (string dic in filtered_dictionaries) {
67
public string[] get_available_locales() {
68
string[] locales = {};
71
string? output = null;
72
GLib.Subprocess p = new GLib.Subprocess.newv({ "locale", "-a" },
73
GLib.SubprocessFlags.STDOUT_PIPE);
74
p.communicate_utf8(null, null, out output, null);
76
foreach (string l in output.split("\n")) {
79
} catch (GLib.Error e) {
87
* Strip the information about the encoding from the locale.
89
* That is, en_US.UTF-8 is mapped to en_US, while en_GB remains
92
public string strip_encoding(string locale) {
93
int dot = locale.index_of_char('.');
94
return locale.substring(0, dot);
97
public string[] get_user_preferred_languages() {
98
GLib.GenericSet<string> dicts = new GLib.GenericSet<string>(GLib.str_hash, GLib.str_equal);
99
foreach (string dict in get_available_dictionaries()) {
103
GLib.GenericSet<string> locales = new GLib.GenericSet<string>(GLib.str_hash, GLib.str_equal);
104
foreach (string locale in get_available_locales()) {
105
locales.add(strip_encoding(locale));
108
string[] output = {};
109
unowned string[] language_names = GLib.Intl.get_language_names();
110
foreach (string lang in language_names) {
111
// Check if we have the associated locale and the dictionary installed before actually
112
// considering this language.
113
if (lang != "C" && dicts.contains(lang) && locales.contains(lang)) {
120
public string? language_name_from_locale (string locale) {
121
if (language_names == null) {
122
language_names = new HashTable<string, string>(GLib.str_hash, GLib.str_equal);
124
unowned Xml.Doc doc = Xml.Parser.parse_file(ISO_CODE_639_XML);
129
unowned Xml.Node root = doc.get_root_element();
130
for (unowned Xml.Node entry = root.children; entry != null; entry = entry.next) {
131
if (entry.type == Xml.ElementType.ELEMENT_NODE) {
132
string? iso_639_1 = null;
133
string? language_name = null;
135
for (unowned Xml.Attr a = entry.properties; a != null; a = a.next) {
137
case "iso_639_1_code":
138
iso_639_1 = a.children->content;
141
language_name = a.children->content;
147
if (language_name != null) {
148
if (iso_639_1 != null) {
149
language_names.insert(iso_639_1, language_name);
158
// Look for the name of language matching only the part before the _
161
pos = locale.index_of_char('_');
164
// Return a translated version of the language.
165
string language_name = GLib.dgettext("iso_639", language_names.get(locale.substring(0, pos)));
167
return language_name;
170
public string? country_name_from_locale(string locale) {
171
if (country_names == null) {
172
country_names = new HashTable<string, string>(GLib.str_hash, GLib.str_equal);
174
unowned Xml.Doc doc = Xml.Parser.parse_file(ISO_CODE_3166_XML);
180
unowned Xml.Node root = doc.get_root_element();
181
for (unowned Xml.Node entry = root.children; entry != null; entry = entry.next) {
182
if (entry.type == Xml.ElementType.ELEMENT_NODE) {
183
string? iso_3166 = null;
184
string? country_name = null;
186
for (unowned Xml.Attr a = entry.properties; a != null; a = a.next) {
189
iso_3166 = a.children->content;
192
country_name = a.children->content;
198
if (country_name != null) {
199
if (iso_3166 != null) {
200
country_names.insert(iso_3166, country_name);
209
// Look for the name of language matching only the part before the _
212
pos = locale.index_of_char('_');
215
string country_name = GLib.dgettext("iso_3166", country_names.get(locale.substring(pos+1)));