2013-04-01 03:33 UTC+0200 Viktor Szakats (harbour syenar.net)

* src/rtl/hbi18n2.prg
    ! __i18n_potArraySort() fixed to not RTE when no source is present
    + __i18n_potArraySort() tweaked to sort items w/o source info to always
      come before ones with source info
    + __i18n_potArrayClean() wlll now call transformation callback before
      deleting items
    + __i18n_potArrayClean() will now remove the item in question if the
      callback returns any non-string value
    + __i18n_potArrayClean()'s second lKeepVoidTranslations = .F. option will
      now also remove items where the translation is identical to the msg id

  - utils/hbmk2/_po_pull.hb
  - utils/hbmk2/_po_push.hb
  - utils/hbmk2/_md_make.hb
  + utils/hbmk2/hblang.hb
    * merged separate management scripts into one
    + sort pulled .po files before stripping/processing
    ! do not process .po files in place to avoid race conditions
      between parallel script sessions
    * minor tweaks

  * README.md
    ! minor
This commit is contained in:
Viktor Szakats
2013-04-01 04:09:18 +02:00
parent 0aadabafd7
commit 02ebccfcdd
6 changed files with 296 additions and 226 deletions

View File

@@ -10,6 +10,31 @@
* Change, ! Fix, % Optimization, + Addition, - Removal, ; Comment
*/
2013-04-01 03:33 UTC+0200 Viktor Szakats (harbour syenar.net)
* src/rtl/hbi18n2.prg
! __i18n_potArraySort() fixed to not RTE when no source is present
+ __i18n_potArraySort() tweaked to sort items w/o source info to always
come before ones with source info
+ __i18n_potArrayClean() wlll now call transformation callback before
deleting items
+ __i18n_potArrayClean() will now remove the item in question if the
callback returns any non-string value
+ __i18n_potArrayClean()'s second lKeepVoidTranslations = .F. option will
now also remove items where the translation is identical to the msg id
- utils/hbmk2/_po_pull.hb
- utils/hbmk2/_po_push.hb
- utils/hbmk2/_md_make.hb
+ utils/hbmk2/hblang.hb
* merged separate management scripts into one
+ sort pulled .po files before stripping/processing
! do not process .po files in place to avoid race conditions
between parallel script sessions
* minor tweaks
* README.md
! minor
2013-04-01 01:35 UTC+0200 Viktor Szakats (harbour syenar.net)
* src/rtl/hbi18n2.prg
* consider the complete original string when sorting (was first 30 chars)

View File

@@ -70,7 +70,7 @@ There are several ways to help making Harbour better:
- Always keep one (not zero or multiple) newline at the end of file
- Use platform native newline (CRLF or LF)
- In the rare case you need to send something large (> 100KB),
use this [free service](http://dropcanvas.com)
use this [free service](http://dropcanvas.com).
- Of course, there is more into Harbour contribution than writing
code, so you're welcome to do so in other areas like documentation,
helping fellow users, giving input on decisions, testing in

View File

@@ -321,31 +321,42 @@ STATIC FUNCTION __i18n_ItemToStr( item )
LOCAL cSource := item[ _I18N_SOURCE ]
LOCAL tmp
hb_default( @cSource, "" )
/* first source occurrence */
IF ( tmp := At( " ", cSource ) ) > 0
cSource := Left( cSource, tmp - 1 )
ENDIF
IF ( tmp := RAt( ":", cSource ) ) > 0
cSource := Left( cSource, tmp - 1 ) + Str( Val( SubStr( cSource, tmp + 1 ) ), 10, 0 )
cSource := "~" + Left( cSource, tmp - 1 ) + Str( Val( SubStr( cSource, tmp + 1 ) ), 10, 0 )
ENDIF
RETURN cSource + item[ _I18N_MSGID, 1 ]
FUNCTION __i18n_potArrayClean( aTrans, lSource, lEmptyTranslations, bTransformTranslation )
FUNCTION __i18n_potArrayClean( aTrans, lKeepSource, lKeepVoidTranslations, bTransformTranslation )
LOCAL item
LOCAL lEmpty
LOCAL cString
hb_default( @lSource, .T. )
hb_default( @lEmptyTranslations, .T. )
hb_default( @lKeepSource, .T. )
hb_default( @lKeepVoidTranslations, .T. )
FOR EACH item IN aTrans DESCEND
IF ! lEmptyTranslations
IF HB_ISEVALITEM( bTransformTranslation )
FOR EACH cString IN item[ _I18N_MSGSTR ]
cString := Eval( bTransformTranslation, cString, item[ _I18N_MSGID, cString:__enumIndex() ] )
IF ! HB_ISSTRING( cString )
hb_ADel( aTrans, item:__enumIndex(), .T. )
LOOP
ENDIF
NEXT
ENDIF
IF ! lKeepVoidTranslations
lEmpty := .T.
FOR EACH cString IN item[ _I18N_MSGSTR ]
IF ! Empty( cString )
IF ! Empty( cString ) .AND. !( cString == item[ _I18N_MSGID, cString:__enumIndex() ] )
lEmpty := .F.
EXIT
ENDIF
@@ -355,12 +366,7 @@ FUNCTION __i18n_potArrayClean( aTrans, lSource, lEmptyTranslations, bTransformTr
LOOP
ENDIF
ENDIF
IF HB_ISEVALITEM( bTransformTranslation )
FOR EACH cString IN item[ _I18N_MSGSTR ]
cString := Eval( bTransformTranslation, cString )
NEXT
ENDIF
IF ! lSource
IF ! lKeepSource
item[ _I18N_SOURCE ] := ""
ENDIF
NEXT

View File

@@ -1,109 +0,0 @@
/*
* Downloads .po files from Transifex localization site
*
* Copyright 2013 Viktor Szakats (harbour syenar.net)
* www - http://harbour-project.org
*
* Requires: curl (built with SSL)
* Reference: http://help.transifex.com/features/api/api-v2.1.html
*
*/
#pragma -w3
PROCEDURE Main( cLogin )
LOCAL cBase := hb_DirBase()
LOCAL json
LOCAL cLang
LOCAL cTemp
LOCAL cProject := "harbour"
LOCAL cMain := cBase + "hbmk2.prg"
LOCAL cPO_Dir := cBase + hb_DirSepToOS( "po/" )
IF Empty( cLogin )
cLogin := GetEnv( "HB_TRANSIFEX_LOGIN" ) /* Format: username:password */
ENDIF
FClose( hb_FTempCreateEx( @cTemp ) )
? "pulling .po files:"
FOR EACH cLang IN hb_ATokens( hb_regexAll( "-lng=([a-zA-Z0-9_\-,]*)", hb_MemoRead( hb_FNameExtSet( cMain, ".hbp" ) ),,,,, .T. )[ 1 ][ 2 ], "," )
?? "", cLang
hb_run( hb_StrFormat( "curl -s -i -L --user %1$s -X " + ;
"GET https://www.transifex.com/api/2/project/%2$s/resource/%3$s/translation/%4$s/" + ;
" -o %5$s", ;
cLogin, cProject, hb_FNameName( cMain ), cLang, cTemp ) )
IF hb_jsonDecode( GetJSON( hb_MemoRead( cTemp ) ), @json ) > 0
hb_MemoWrit( cPO_Dir + hb_FNameName( cMain ) + "." + cLang + ".po", json[ "content" ] )
/* should only do this if the translation is primarily done
on Transifex website. This encouraged and probably the case
in practice. Delete source information, delete empty
translations and apply some automatic transformation for
common translation mistakes. */
PO_Clean( cPO_Dir + hb_FNameName( cMain ) + "." + cLang + ".po", .F., .F., @DoctorTranslation() )
ELSE
? "API error"
ENDIF
NEXT
FErase( cTemp )
RETURN
STATIC FUNCTION DoctorTranslation( cString )
cString := Unspace( AllTrim( cString ) )
cString := StrTran( cString, hb_UChar( 0x23CE ), e"\n" )
cString := StrTran( cString, e"\n ", e"\n" )
cString := StrTran( cString, "( ", "(" )
cString := StrTran( cString, " )", ")" )
RETURN cString
/* Converts multiple spaces to just one */
STATIC FUNCTION Unspace( cString )
LOCAL cResult := ""
LOCAL cChar, cCharPrev
LOCAL tmp
FOR tmp := 1 TO Len( cString )
cChar := SubStr( cString, tmp, 1 )
IF !( cChar == " " ) .OR. !( cCharPrev == " " )
cResult += cChar
ENDIF
cCharPrev := cChar
NEXT
RETURN cResult
STATIC FUNCTION PO_Clean( cFileName, ... )
LOCAL aTrans
LOCAL cErrorMsg
IF ( aTrans := __i18n_potArrayLoad( cFileName, @cErrorMsg ) ) != NIL .AND. ;
__i18n_potArraySave( cFileName, __i18n_potArrayClean( aTrans, ... ), @cErrorMsg )
RETURN .T.
ENDIF
? cErrorMsg
RETURN .F.
STATIC FUNCTION GetJSON( cString )
cString := SubStr( cString, At( "{", cString ) )
cString := Left( cString, RAt( "}", cString ) )
RETURN cString

View File

@@ -1,98 +0,0 @@
/*
* Uploads source .pot file to Transifex localization site
*
* Copyright 2013 Viktor Szakats (harbour syenar.net)
* www - http://harbour-project.org
*
* Requires: curl (built with SSL), Harbour in PATH
* Reference: http://help.transifex.com/features/api/api-v2.1.html
*
*/
#pragma -w3
PROCEDURE Main( cLogin )
LOCAL cBase := hb_DirBase()
LOCAL json
LOCAL cTemp, cTemp2
LOCAL cContent
LOCAL cProject := "harbour"
LOCAL cMain := cBase + "hbmk2.prg"
LOCAL cBaseLang := "en"
LOCAL cPO_Dir := cBase + hb_DirSepToOS( "po/" )
IF Empty( cLogin )
cLogin := GetEnv( "HB_TRANSIFEX_LOGIN" ) /* Format: username:password */
ENDIF
FClose( hb_FTempCreateEx( @cTemp, , , ".pot" ) )
FClose( hb_FTempCreateEx( @cTemp2 ) )
? "generating .pot"
hb_run( hb_StrFormat( "hbmk2 -hbraw -q0 %1$s -j%2$s -s", cMain, cTemp ) )
? "sorting .pot"
POT_Sort( cTemp )
? "saving locally"
cContent := hb_StrFormat( ;
'#, c-format' + hb_eol() + ;
'msgid ""' + hb_eol() + ;
'msgstr ""' + hb_eol() + ;
'"Project-Id-Version: %1$s\n"' + hb_eol() + ;
'"Language: %2$s\n"' + hb_eol() + ;
'"MIME-Version: 1.0\n"' + hb_eol() + ;
'"Content-Type: text/plain; charset=UTF-8\n"' + hb_eol() + ;
'"Content-Transfer-Encoding: 8bit\n"', hb_FNameName( cMain ), cBaseLang ) + hb_eol() + ;
hb_eol() + ;
hb_MemoRead( cTemp )
hb_MemoWrit( cPO_Dir + hb_FNameName( cMain ) + "." + cBaseLang + ".po", cContent )
? "uploading", "size", Len( cContent )
hb_MemoWrit( cTemp, hb_jsonEncode( { "content" => StrTran( cContent, hb_eol(), e"\n" ) } ) )
hb_run( hb_StrFormat( 'curl -s -i -L --user %1$s -X ' + ;
'PUT -d @%2$s -H "Content-Type: application/json" ' + ;
'https://www.transifex.com/api/2/project/%3$s/resource/%4$s/content/' + ;
' -o %5$s', ;
cLogin, cTemp, cProject, hb_FNameName( cMain ), cTemp2 ) )
IF hb_jsonDecode( GetJSON( hb_MemoRead( cTemp2 ) ), @json ) > 0
? hb_ValToExp( json )
ELSE
? "API error"
ENDIF
FErase( cTemp )
FErase( cTemp2 )
RETURN
STATIC FUNCTION GetJSON( cString )
cString := SubStr( cString, At( "{", cString ) )
cString := Left( cString, RAt( "}", cString ) )
RETURN cString
STATIC FUNCTION POT_Sort( cFileName )
LOCAL aTrans
LOCAL cErrorMsg
IF ( aTrans := __i18n_potArrayLoad( cFileName, @cErrorMsg ) ) != NIL .AND. ;
__i18n_potArraySave( cFileName, __i18n_potArraySort( aTrans ), @cErrorMsg )
RETURN .T.
ENDIF
? cErrorMsg
RETURN .F.

View File

@@ -1,10 +1,11 @@
/*
* Generates .md help files for all languages
* Manages translations and automatic doc generation
*
* Copyright 2013 Viktor Szakats (harbour syenar.net)
* www - http://harbour-project.org
*
* Requires: Harbour in PATH
* Requires: curl (built with SSL), Harbour in PATH
* Reference: http://help.transifex.com/features/api/api-v2.1.html
*
*/
@@ -12,7 +13,26 @@
#include "directry.ch"
PROCEDURE Main()
PROCEDURE Main( cCommand, ... )
LOCAL hCommand := { ;
"doc_make" => @doc_make(), ; /* Generate doc files for all languages */
"src_push" => @src_push(), ; /* Upload translation source to Transifex localization service */
"trs_pull" => @trs_pull(), ; /* Download translations from Transifex localization service */
"trs_push" => @trs_push() } /* Upload local translations to Transifex localization service */
IF ! Empty( cCommand ) .AND. cCommand $ hCommand
Set( _SET_DEFEXTENSIONS, .F. )
Eval( hCommand[ cCommand ], ... )
ELSE
? "unknown command"
ENDIF
RETURN
/* --------------------------------------------- */
STATIC PROCEDURE doc_make()
LOCAL cBase := hb_DirBase()
@@ -28,7 +48,7 @@ PROCEDURE Main()
cTemp := hb_FNameExtSet( cMain, ".hrb" )
? "generating .md help:"
? "generating documentation:"
hb_run( hb_StrFormat( "hbmk2 -hbraw -q0 %1$s -gh -o%2$s", cMain, cTemp ) )
@@ -46,8 +66,8 @@ PROCEDURE Main()
/* special case */
IF hb_FNameName( cMain ) == "hbmk2"
file := cBase + hb_DirSepToOS( "../../contrib/hbrun/doc/" ) + "hbrun" + "." + cLang + ".md"
hb_run( hb_StrFormat( "hbrun %1$s -lang=%2$s -longhelpmdsh > %3$s", cTemp, cLang, file ) )
file := hb_FNameDir( cMain ) + hb_DirSepToOS( "../../contrib/hbrun/doc/" ) + "hbrun" + "." + cLang + ".md"
hb_run( hb_StrFormat( "hbrun %1$s %2$s > %3$s", cTemp, StrTran( "-lang={LANG} -longhelpmdsh", "{LANG}", cLang ), file ) )
FToNativeEOL( file )
ENDIF
@@ -60,3 +80,229 @@ PROCEDURE Main()
STATIC FUNCTION FToNativeEOL( cFile )
RETURN hb_MemoWrit( cFile, StrTran( hb_MemoRead( cFile ), e"\n", hb_eol() ) )
/* --------------------------------------------- */
STATIC PROCEDURE src_push( cLogin )
LOCAL cBase := hb_DirBase()
LOCAL json
LOCAL cTemp, cTemp2
LOCAL cContent
LOCAL cProject := "harbour"
LOCAL cMain := cBase + "hbmk2.prg"
LOCAL cBaseLang := "en"
LOCAL cPO_Dir := cBase + hb_DirSepToOS( "po/" )
IF Empty( cLogin )
cLogin := GetEnv( "HB_TRANSIFEX_LOGIN" ) /* Format: username:password */
ENDIF
FClose( hb_FTempCreateEx( @cTemp, , , ".pot" ) )
FClose( hb_FTempCreateEx( @cTemp2 ) )
? "generating translation source"
hb_run( hb_StrFormat( "hbmk2 -hbraw -q0 %1$s -j%2$s -s", cMain, cTemp ) )
POT_Sort( cTemp )
? "saving locally"
cContent := hb_StrFormat( ;
'#, c-format' + hb_eol() + ;
'msgid ""' + hb_eol() + ;
'msgstr ""' + hb_eol() + ;
'"Project-Id-Version: %1$s\n"' + hb_eol() + ;
'"Language: %2$s\n"' + hb_eol() + ;
'"MIME-Version: 1.0\n"' + hb_eol() + ;
'"Content-Type: text/plain; charset=UTF-8\n"' + hb_eol() + ;
'"Content-Transfer-Encoding: 8bit\n"', hb_FNameName( cMain ), cBaseLang ) + hb_eol() + ;
hb_eol() + ;
hb_MemoRead( cTemp )
hb_MemoWrit( cPO_Dir + hb_FNameName( cMain ) + "." + cBaseLang + ".po", cContent )
? "uploading", "size", hb_ntos( Len( cContent ) )
hb_MemoWrit( cTemp, hb_jsonEncode( { "content" => StrTran( cContent, hb_eol(), e"\n" ) } ) )
hb_run( hb_StrFormat( 'curl -s -i -L --user %1$s -X ' + ;
'PUT -d @%2$s -H "Content-Type: application/json" ' + ;
'https://www.transifex.com/api/2/project/%3$s/resource/%4$s/content/' + ;
' -o %5$s', ;
cLogin, cTemp, cProject, hb_FNameName( cMain ), cTemp2 ) )
IF hb_jsonDecode( GetJSON( hb_MemoRead( cTemp2 ) ), @json ) > 0
? hb_ValToExp( json )
ELSE
? "API error"
ENDIF
FErase( cTemp )
FErase( cTemp2 )
RETURN
STATIC FUNCTION POT_Sort( cFileName )
LOCAL aTrans
LOCAL cErrorMsg
IF ( aTrans := __i18n_potArrayLoad( cFileName, @cErrorMsg ) ) != NIL .AND. ;
__i18n_potArraySave( cFileName, __i18n_potArraySort( aTrans ), @cErrorMsg )
RETURN .T.
ENDIF
? "i18n error", cErrorMsg
RETURN .F.
/* --------------------------------------------- */
STATIC PROCEDURE trs_pull( cLogin )
LOCAL cBase := hb_DirBase()
LOCAL json
LOCAL cLang
LOCAL cTemp
LOCAL cProject := "harbour"
LOCAL cMain := cBase + "hbmk2.prg"
LOCAL cPO_Dir := cBase + hb_DirSepToOS( "po/" )
IF Empty( cLogin )
cLogin := GetEnv( "HB_TRANSIFEX_LOGIN" ) /* Format: username:password */
ENDIF
FClose( hb_FTempCreateEx( @cTemp ) )
? "pulling translations:"
FOR EACH cLang IN hb_ATokens( hb_regexAll( "-lng=([a-zA-Z0-9_\-,]*)", hb_MemoRead( hb_FNameExtSet( cMain, ".hbp" ) ),,,,, .T. )[ 1 ][ 2 ], "," )
?? "", cLang
hb_run( hb_StrFormat( "curl -s -i -L --user %1$s -X " + ;
"GET https://www.transifex.com/api/2/project/%2$s/resource/%3$s/translation/%4$s/" + ;
" -o %5$s", ;
cLogin, cProject, hb_FNameName( cMain ), cLang, cTemp ) )
IF hb_jsonDecode( GetJSON( hb_MemoRead( cTemp ) ), @json ) > 0
hb_MemoWrit( cTemp, json[ "content" ] )
POT_Sort( cTemp )
/* should only do this if the translation is primarily done
on Transifex website. This encouraged and probably the case
in practice. Delete source information, delete empty
translations and apply some automatic transformation for
common translation mistakes. */
PO_Clean( cTemp, cPO_Dir + hb_FNameName( cMain ) + "." + cLang + ".po", .F., .F., @DoctorTranslation() )
ELSE
? "API error"
ENDIF
NEXT
FErase( cTemp )
RETURN
STATIC FUNCTION DoctorTranslation( cString )
cString := StrUnspace( AllTrim( cString ) )
cString := StrTran( cString, hb_UChar( 0x23CE ), e"\n" )
cString := StrTran( cString, e"\n ", e"\n" )
cString := StrTran( cString, "( ", "(" )
cString := StrTran( cString, " )", ")" )
RETURN cString
/* Converts multiple spaces to just one */
STATIC FUNCTION StrUnspace( cString )
LOCAL cResult := ""
LOCAL cChar, cCharPrev
LOCAL tmp
FOR tmp := 1 TO Len( cString )
cChar := SubStr( cString, tmp, 1 )
IF !( cChar == " " ) .OR. !( cCharPrev == " " )
cResult += cChar
ENDIF
cCharPrev := cChar
NEXT
RETURN cResult
STATIC FUNCTION PO_Clean( cFNSource, cFNTarget, ... )
LOCAL aTrans
LOCAL cErrorMsg
IF ( aTrans := __i18n_potArrayLoad( cFNSource, @cErrorMsg ) ) != NIL .AND. ;
__i18n_potArraySave( cFNTarget, __i18n_potArrayClean( aTrans, ... ), @cErrorMsg )
RETURN .T.
ENDIF
? "i18n error", cErrorMsg
RETURN .F.
/* --------------------------------------------- */
STATIC PROCEDURE trs_push( cLogin )
LOCAL cBase := hb_DirBase()
LOCAL json
LOCAL cTemp, cTemp2
LOCAL cContent
LOCAL cProject := "harbour"
LOCAL cMain := cBase + "hbmk2.prg"
LOCAL cLang := "hu"
LOCAL cPO_Dir := cBase + hb_DirSepToOS( "po/" )
IF Empty( cLogin )
cLogin := GetEnv( "HB_TRANSIFEX_LOGIN" ) /* Format: username:password */
ENDIF
FClose( hb_FTempCreateEx( @cTemp ) )
FClose( hb_FTempCreateEx( @cTemp2 ) )
cContent := hb_MemoRead( cPO_Dir + hb_FNameName( cMain ) + "." + cLang + ".po" )
? "uploading translation", "size", Len( cContent )
hb_MemoWrit( cTemp, hb_jsonEncode( { "content" => StrTran( cContent, hb_eol(), e"\n" ) } ) )
hb_run( hb_StrFormat( 'curl -s -i -L --user %1$s -X ' + ;
'PUT -d @%2$s -H "Content-Type: application/json" ' + ;
'https://www.transifex.com/api/2/project/%3$s/resource/%4$s/translation/%5$s/' + ;
' -o %6$s', ;
cLogin, cTemp, cProject, hb_FNameName( cMain ), cLang, cTemp2 ) )
IF hb_jsonDecode( GetJSON( hb_MemoRead( cTemp2 ) ), @json ) > 0
? hb_ValToExp( json )
ELSE
? "API error"
ENDIF
FErase( cTemp )
FErase( cTemp2 )
RETURN
/* --------------------------------------------- */
STATIC FUNCTION GetJSON( cString )
cString := SubStr( cString, At( "{", cString ) )
cString := Left( cString, RAt( "}", cString ) )
RETURN cString