Polib.Net, an open-source .NET Solution for managing multi-lingual apps

Polib.Net, an open-source .NET Solution for managing multi-lingual apps

Hi guys, I'm back on Github for more. This time, I've been writing a simple yet powerful solution to handle translation files, the Uniforum .po style. Yes, that's right: the popular translation file format we - as developers - love even more than the .NET Framework's built-in ResourceManager. Just look at WordPress and see how folks used it there. Now the same simplicity and elegance within any of your .NET apps.

Check out my GitHub page for this new stuff: https://github.com/bigabdoul/Polib.Net. You'll love it, I promise! But for now, here's what README.md says:

What is Polib.Net?

Have you ever wanted to make multi-lingual apps but were bored with .NET's way of doing it using ResourceManager? ResourceManager is good, but one of its drawbacks is that you have to recompile (over and over again) your translations into binary files and re-deploy them whenever you add new culture-dependent bits to the app. But that's only the easy part. Managing translation files becomes a nightmare when you want to work as a team on the translations.

Instead of relying on ResourceManager, there're other ways to support multiple cultures, such as the popular PO translations file format. The PO (.po) files make it easy to support multiple languages within your applications.

The main goal of the Polib.Net Solution is to use a set of libraries to use and manage .po translation files within a .NET application in a simple yet effective way. By simple and effective, I mean that you should be able to add new translations and make them appear in your apps on the fly, just by updating your .po files, without recompiling the whole stuff into binaries and re-deploy it. Check out the "Polib.NetCore.Web" project in the demos folder, an ASP.NET Core Web application under development to showcase this feature.

Getting started

To get up and running, do the following:

  • Clone this repository into a directory (e.g. 'github-projects'):

$ git clone https://github.com/bigabdoul/Polib.Net.git

This action will create a subdirectory 'Polib.Net' in 'github-projects' like so: 'github-projects/Polib.net'

  • Clone this demo-dependent project also into 'github-projects' like so:

$ git clone https://github.com/bigabdoul/Bootstrap.Themes.git

The Polib.Net Solution does not require this project but is used in the demo web app and is a fancy way to change its appearence using many pre-built Bootstrap themes.

  • Open the solution file Polib.Net.sln and build the solution. Once the dependencies restored, in the 'demos' Solution Folder, set the project 'Polib.NetCore.Web' as the StartUp Project, then hit F5 to run it.

This should keep you going for now.

Exploring the Solution

The Solution has 4 folders, namely demosSolution Itemssrc, and test.

The src folder

It contains three projects:

  1. Polib.NetStandard: This is the core project targeting .NET Standard 1.4, which means it's a cross-platform implementation which can run on Linux, MAC, and Windows Universal Platform. It's responsible for all interactions with .po translation files.
  2. Polib.NetCore.Mvc: This one is a translation library project targeting .NET Core 2.1 for use within an ASP.NET Core MVC application.
  3. Polib.Net.Mvc: Like the one above, it's a translation library project for ASP.NET MVC but targeting .NET Framework 4.6.1.

The test folder

One project with a couple of unit tests is available for now. I have tested only the core features of the solution. And I expect to do many more tests in the near future.

The demos folder

As previously mentioned, this folder contains the "Polib.NetCore.Web" project, which showcases an ASP.NET Core MVC web application making use of the Polib.NetStandardPolib.NetCore.Mvc, and Bootstrap.Themes projects. For simplicity, it uses AngularJS to provide client-side functionalities, such as making Web API calls and displaying the results in the browser.

The Solution Items folder

This folder contains items without functional impact on the Solution itself, such as this README.md and other files. It just makes it easier to organize the non-vital items that are part of the Solution.

Examples

After running the demo app, let's look at how you can integrate the libraries into a new project. For example, to build a multi-lingual console app:

  1. In Visual Studio or Visual Studio Code, create a new console project.
  2. Add a reference to the Polib.NetStandard project, or the Polib.Net.dll file.
  3. In your console app, add the following using statements at the top of the 'Program.cs' file:

using Polib.Net;
using System;        

  1. In the public static void Main(string[] args) method, add the following:

// get a reference to the default translation manager instance
var manager = TranslationManager.Instance;
manager.PoFilesDirectory = "path/to/po/files";

var result = manager.TranslatePlural("fr-FR"
 , "{0} media file restored from the trash."
 , "{0} media files restored from the trash.", 3, 3);

 Console.WriteLine(result);        

Of course, you may wonder: "Where are the translation files? Do I have to write them manually?" We have good news: you can use the GNU Gettext tools to extract translatable strings from your source code (like the one above) and generate .po template files. On Windows, you can grab one of these tools from here: https://mlocati.github.io/articles/gettext-iconv-windows.html

Once downloaded and installed the binaries of gettext, you can execute the following at the command prompt (supposing that the current directory is where 'Program.cs' is):

xgettext.exe -k -kTranslatePlural:2,3 --from-code=UTF-8 -LC# --omit-header -omessages.pot Program.cs        

More on that command right now! First, there's 'xgettext.exe,' a command-line tool to extract translatable strings from source code. It supports multiple programming languages, one of which is C# (hence the -LC# switch).

The -k option stands for 'keyword,' meaning the method name used to translate strings. In our case, it's the 'TranslatePlural' method, hence '-kTranslatePlural.' Then this is followed by a colon ':' and the numbers 2 (second parameter, which identifies the singular-form message) and 3 (third parameter, which identifies the plural message). The first '-k' option indicates that the xgettext program should not provide its own default keywords.

The -o (output) option followed by 'messages.pot' indicates the .pot (PO template) file to write on disk as the result of parsing our source code.

Program.cs is the name of the source code file we want to scan. We could have replaced this by a list of files specified in another file (that would be the -f option ).

The -f (file) input option indicates the file to scan. This option could be a list of files or another file that contains the list of files to scan. But here, we are using just 'Program.cs.'

This command should generate a 'messages.pot' file in the same directory as the 'Program.cs.' If you open this file with a program like "Poedit," you can effortlessly start translations. In our Main(string args[]) method above, you can rename the file's extension to .po and replace manager.PoFilesDirectory = "path/to/po/files"; with the directory name that messages.po. Or, you could load this messages.po files:

using Polib.Net.IO;
using System;
using System.Collections.Generic;
using System.IO;


namespace Polib.Net.Demos
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                if (args.Length < 1)
                {
                    Console.WriteLine("Please enter the translation file name");
                    return;
                }


                // get the file name
                var filename = args[0];


                if (!File.Exists(filename))
                {
                    Console.WriteLine("File '{0}' does not exist!", filename);
                    return;
                }


                // the culture we're handling
                var culture = "fr-FR";


                // read the translation file; make sure to set the full path
                var catalog = PoFileReader.Read(filename, culture);


                // get a reference to the default translation manager instance
                var manager = TranslationManager.Instance;


                // add the catalog to the translation manager's catalogs dictionary;
                // we could add as many different cultures as we want to support;
                manager.Catalogs.Add(culture, new[] { catalog });


                // use a generic list instead of an array if you intend to monitor
                // changes that occur within the file system in the directory containing
                // the translation files, like so:
                //manager.Catalogs.Add(culture, new List<ICatalog> { catalog });


                // should come as a parameter from somewhere
                var filesRestored = 3;


                // translate the message and format the output on the fly
                var result = manager.TranslatePlural(culture
                    , "{0} media file restored from the trash."
                    , "{0} media files restored from the trash."
                    , filesRestored
                    , filesRestored);


                Console.WriteLine(result);
            }
            catch (Exception ex)
            {
                Console.WriteLine("The following error occured: {0}", ex);
            }
        }
    }
}        

To see how you can extract translation strings from your source code, look in the Solution Items folder for an item named 'genpot.bat.' It operates on the web app demo project by extracting translation strings from the models, views, and controllers and generating .pot files in a 'temp' directory. Should I have more time, I'll write a command-line app that does all this and more (such as merging translation strings from the source into existing .po files). The command-line app should run automatically after successfully building the demo web app.

More will be coming soon. The best way to stay current is to follow my GitHub activities: https://github.com/bigabdoul. Comments and suggestions are very appreciated, as usual.

To view or add a comment, sign in

More articles by Abdourahamane Kaba

Others also viewed

Explore content categories