[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [61205] trunk/blender: Be ready for changes in bf-translations repository

Sergey Sharybin sergey.vfx at gmail.com
Fri Nov 8 21:44:49 CET 2013


Revision: 61205
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=61205
Author:   nazgul
Date:     2013-11-08 20:44:48 +0000 (Fri, 08 Nov 2013)
Log Message:
-----------
Be ready for changes in bf-translations repository

Made it so if there's release/datafiles/locale/po
folder, then all the .po files will be converted
to .mo at blender compile time and installed to
an appropriate location.

Uses small own implementation msgfmt which is
based on msgfmt.py from Python project, but also
supports contexts.

There's no functional changes for until we've
switched to use source .po files instead of
pre-compiled .mo.

P.S. Well, there's one change which is msgfmt.cc
     being compiled even if it's not used, but
     would rather not clutter code with checks
     since pretty soon we'll use this program
     anyway.

Modified Paths:
--------------
    trunk/blender/SConstruct
    trunk/blender/build_files/cmake/macros.cmake
    trunk/blender/intern/locale/CMakeLists.txt
    trunk/blender/intern/locale/SConscript
    trunk/blender/source/creator/CMakeLists.txt

Added Paths:
-----------
    trunk/blender/intern/locale/msgfmt.cc

Modified: trunk/blender/SConstruct
===================================================================
--- trunk/blender/SConstruct	2013-11-08 18:01:07 UTC (rev 61204)
+++ trunk/blender/SConstruct	2013-11-08 20:44:48 UTC (rev 61205)
@@ -944,6 +944,9 @@
         def check_path(path, member):
             return (member in path.split(os.sep))
 
+        po_dir = os.path.join("release", "datafiles", "locale", "po")
+        need_compile_mo = os.path.exists(po_dir)
+
         for intpath in internationalpaths:
             for dp, dn, df in os.walk(intpath):
                 if '.svn' in dn:
@@ -952,7 +955,10 @@
                     dn.remove('_svn')
 
                 # we only care about release/datafiles/fonts, release/datafiles/locales
-                if check_path(dp, "fonts") or check_path(dp, "locale"):
+                if check_path(dp, "locale"):
+                    if need_compile_mo and check_path(dp, "po"):
+                        continue
+                elif check_path(dp, "fonts"):
                     pass
                 else:
                     continue
@@ -966,6 +972,19 @@
                     env.Execute(Mkdir(dir))
                 scriptinstall.append(env.Install(dir=dir,source=source))
 
+        if need_compile_mo:
+            for f in os.listdir(po_dir):
+                if not f.endswith(".po"):
+                    continue
+
+                locale_name = os.path.splitext(f)[0]
+
+                mo_file = os.path.join(B.root_build_dir, "locale", locale_name + ".mo")
+
+                dir = os.path.join(env['BF_INSTALLDIR'], VERSION)
+                dir = os.path.join(dir, "datafiles", "locale", locale_name, "LC_MESSAGES")
+                scriptinstall.append(env.InstallAs(os.path.join(dir, "blender.mo"), mo_file))
+
 #-- icons
 if env['OURPLATFORM']=='linux':
     iconlist = []

Modified: trunk/blender/build_files/cmake/macros.cmake
===================================================================
--- trunk/blender/build_files/cmake/macros.cmake	2013-11-08 18:01:07 UTC (rev 61204)
+++ trunk/blender/build_files/cmake/macros.cmake	2013-11-08 20:44:48 UTC (rev 61205)
@@ -870,3 +870,33 @@
 	unset(_file_to)
 
 endmacro()
+
+macro(msgfmt_simple
+      file_from
+      list_to_add)
+
+	# remove ../'s
+	get_filename_component(_file_from_we ${file_from} NAME_WE)
+
+	get_filename_component(_file_from ${file_from} REALPATH)
+	get_filename_component(_file_to ${CMAKE_CURRENT_BINARY_DIR}/${_file_from_we}.mo REALPATH)
+
+	list(APPEND ${list_to_add} ${_file_to})
+
+	get_filename_component(_file_to_path ${_file_to} PATH)
+
+	add_custom_command(
+		OUTPUT  ${_file_to}
+		COMMAND ${CMAKE_COMMAND} -E make_directory ${_file_to_path}
+		COMMAND ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/msgfmt ${_file_from} ${_file_to}
+		DEPENDS msgfmt)
+
+	message("${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/msgfmt ${_file_from} ${_file_to}")
+
+	set_source_files_properties(${_file_to} PROPERTIES GENERATED TRUE)
+
+	unset(_file_from_we)
+	unset(_file_from)
+	unset(_file_to)
+	unset(_file_to_path)
+endmacro()

Modified: trunk/blender/intern/locale/CMakeLists.txt
===================================================================
--- trunk/blender/intern/locale/CMakeLists.txt	2013-11-08 18:01:07 UTC (rev 61204)
+++ trunk/blender/intern/locale/CMakeLists.txt	2013-11-08 20:44:48 UTC (rev 61205)
@@ -45,3 +45,11 @@
 endif()
 
 blender_add_lib(bf_intern_locale "${SRC}" "${INC}" "${INC_SYS}")
+
+# -----------------------------------------------------------------------------
+# Build msgfmt executable
+set(MSFFMT_SRC
+	msgfmt.cc
+)
+
+add_executable(msgfmt ${MSFFMT_SRC})

Modified: trunk/blender/intern/locale/SConscript
===================================================================
--- trunk/blender/intern/locale/SConscript	2013-11-08 18:01:07 UTC (rev 61204)
+++ trunk/blender/intern/locale/SConscript	2013-11-08 20:44:48 UTC (rev 61205)
@@ -37,3 +37,49 @@
     incs += ' ' + env['BF_BOOST_INC']
 
 env.BlenderLib( 'bf_intern_locale', sources, Split(incs), defs, libtype=['intern','player'], priority=[10, 185])
+
+if env['WITH_BF_INTERNATIONAL']:
+    import os
+    from os.path import dirname
+
+    def normpath(path):
+        return os.path.abspath(os.path.normpath(path))
+
+    # build directory
+    source_dir = Dir('.').srcnode().path
+    root_build_dir = normpath(env['BF_BUILDDIR'])
+    root_source_dir = dirname(dirname(normpath(source_dir)))
+    po_dir = os.path.join(root_source_dir, "release", "datafiles", "locale", "po")
+    build_dir = os.path.join(root_build_dir, 'locale')
+
+    if os.path.exists(po_dir):
+        # create directory if needed
+        if not os.path.exists(build_dir):
+            os.makedirs(build_dir)
+
+        msgfmt_tool = env.Clone()
+        targetpath = root_build_dir + '/msgfmt'
+        msgfmt_target = msgfmt_tool.Program(target = targetpath, source = ['msgfmt.cc'])
+
+        locale = env.Clone()
+
+        # dependencies
+        dependencies = [targetpath]
+
+        # add command for each locale
+        all_mo_files = []
+        for f in os.listdir(po_dir):
+            if not f.endswith(".po"):
+                continue
+
+            po_file = os.path.join(po_dir, f)
+            mo_file = os.path.join(build_dir, os.path.splitext(f)[0] + ".mo")
+
+            command = "\"%s\" \"%s\" \"%s\"" % (targetpath, po_file, mo_file)
+
+            locale.Command(mo_file, po_file, command)
+            locale.Depends(mo_file, dependencies)
+
+            all_mo_files.append(mo_file)
+
+        env.Depends("boost_locale_wrapper.cpp", all_mo_files)

Added: trunk/blender/intern/locale/msgfmt.cc
===================================================================
--- trunk/blender/intern/locale/msgfmt.cc	                        (rev 0)
+++ trunk/blender/intern/locale/msgfmt.cc	2013-11-08 20:44:48 UTC (rev 61205)
@@ -0,0 +1,366 @@
+// Written by Sergey Sharybin <sergey.vfx at gmail.com>
+// Added support for contexts
+//
+// Based on Python script msgfmt.py from Python source
+// code tree, which wes weitten by Written by
+// Martin v. Löwis <loewis at informatik.hu-berlin.de>
+//
+// Generate binary message catalog from textual translation description.
+//
+// This program converts a textual Uniforum-style message catalog (.po file) into
+// a binary GNU catalog (.mo file).  This is essentially the same function as the
+// GNU msgfmt program, however, it is a simpler implementation.
+//
+// Usage: msgfmt input.po output.po
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <stdlib.h>
+#include <string>
+#include <vector>
+
+namespace {
+
+std::map<std::string, std::string> MESSAGES;
+
+bool starts_with(const std::string &string,
+                 const std::string &prefix) {
+  return prefix.size() <= string.size() &&
+         string.compare(0, prefix.size(), prefix) == 0;
+}
+
+std::string ltrim(const std::string &s) {
+  std::string result = s;
+  result.erase(result.begin(),
+               std::find_if(result.begin(),
+                            result.end(),
+                            std::not1(std::ptr_fun<int, int>(std::isspace))));
+  return result;
+}
+
+std::string rtrim(const std::string &s) {
+  std::string result = s;
+  result.erase(
+    std::find_if(result.rbegin(),
+                 result.rend(),
+                 std::not1(std::ptr_fun<int, int>(std::isspace))).base(),
+    result.end());
+  return result;
+}
+
+std::string trim(const std::string &s) {
+  return ltrim(rtrim(s));
+}
+
+std::string unescape(const std::string &s) {
+  std::string result;
+  std::string::const_iterator it = s.begin();
+  while (it != s.end()) {
+    char current_char = *it++;
+    if (current_char == '\\' && it != s.end()) {
+      char next_char = *it++;
+      if (next_char == '\\') {
+        current_char = '\\';
+      } else if (next_char == 'n') {
+        current_char = '\n';
+      } else if (next_char == 't') {
+        current_char = '\t';
+      } else {
+        current_char = next_char;
+      }
+    }
+    result += current_char;
+  }
+
+  if (result[0] == '"' && result[result.size() - 1] == '"') {
+    result = result.substr(1, result.size() - 2);
+  }
+
+  return result;
+}
+
+// Add a non-fuzzy translation to the dictionary.
+void add(const std::string &msgctxt,
+         const std::string &msgid,
+         const std::string &msgstr,
+         bool fuzzy) {
+  if (fuzzy == false && msgstr.empty() == false) {
+    if (msgctxt.empty()) {
+      MESSAGES[msgid] = msgstr;
+    } else {
+      MESSAGES[msgctxt + (char)0x04 + msgid] = msgstr;
+    }
+  }
+}
+
+template<typename TKey, typename TValue>
+void get_keys(std::map<TKey, TValue> map,
+              std::vector<TKey> *keys) {
+  for (typename std::map<TKey, TValue>::iterator it = map.begin();
+      it != map.end();
+      it++) {
+    keys->push_back(it->first);
+  }
+}
+
+std::string intToBytes(int value) {
+  std::string result;
+  for (unsigned int i = 0; i < sizeof(value); i++) {
+    result += (unsigned char) ((value >> (i * 8)) & 0xff);
+  }
+  return result;
+}
+
+typedef enum {
+  SECTION_NONE = 0,
+  SECTION_CTX  = 1,
+  SECTION_ID   = 2,
+  SECTION_STR  = 3
+} eSectionType;
+
+struct Offset {
+  unsigned int o1, l1, o2, l2;
+};
+
+// Return the generated output.
+std::string generate(void) {
+  // The keys are sorted in the .mo file
+  std::vector<std::string> keys;
+
+  // Get list of sorted keys.
+  get_keys(MESSAGES, &keys);
+  std::sort(keys.begin(), keys.end());
+
+  std::vector<Offset> offsets;
+  std::string ids = "", strs = "";
+  for (std::vector<std::string>::iterator it = keys.begin();
+       it != keys.end();
+       it++) {
+    std::string &id = *it;
+    // For each string, we need size and file offset.  Each string is NUL
+    // terminated; the NUL does not count into the size.
+    Offset offset = {(unsigned int) ids.size(),
+                     (unsigned int) id.size(),
+                     (unsigned int) strs.size(),
+                     (unsigned int) MESSAGES[id].size()};
+    offsets.push_back(offset);
+    ids += id + '\0';
+    strs += MESSAGES[id] + '\0';
+  }
+
+  // The header is 7 32-bit unsigned integers.  We don't use hash tables, so
+  // the keys start right after the index tables.
+  // translated string.
+  int keystart = 7 * 4 + 16 * keys.size();
+  // and the values start after the keys
+  int valuestart = keystart + ids.size();
+  std::vector<int> koffsets;
+  std::vector<int> voffsets;
+  // The string table first has the list of keys, then the list of values.

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list