multi languages, in backend only for the moment
[booh] / bin / booh-backend
index df3ad11d4406a2d80d84ece3579390fb647aca45..3effa7f5da3b9b4d036f3eefd7dbac49c1c749da 100755 (executable)
@@ -31,6 +31,8 @@ require 'booh/html-merges'
 #- bind text domain as soon as possible because some _() functions are called early to build data structures
 bindtextdomain("booh")
 
+SUPPORTED_LANGUAGES = %w(en de fr ja eo)
+
 #- options
 $options = [
     [ '--help',          '-h', GetoptLong::NO_ARGUMENT,       _("Get help message") ],
@@ -51,6 +53,7 @@ $options = [
     [ '--force',         '-f', GetoptLong::NO_ARGUMENT,       _("Force generation of album even if the GUI marked some directories as already generated") ],
 
     [ '--sizes',         '-S', GetoptLong::REQUIRED_ARGUMENT, _("Specify the list of images sizes to use instead of all specified in the theme (this is a comma-separated list)") ],
+    [ '--multi-languages', '-L', GetoptLong::REQUIRED_ARGUMENT, _("Specify the list of languages to support (uses Apache MultiViews); this is a comma-separated list of supported languages, with last element used as the default language; for example: 'fr,eo,en,en'; supported languages: %s") % SUPPORTED_LANGUAGES.join(', ') ],
     [ '--thumbnails-per-row', '-T', GetoptLong::REQUIRED_ARGUMENT, _("Specify the amount of thumbnails per row in the thumbnails page (if applicable in theme)") ],
     [ '--thumbnails-per-page', '-p', GetoptLong::REQUIRED_ARGUMENT, _("Specify the amount of thumbnails per page in the thumbnails page, after which split occurs") ],
     [ '--optimize-for-32', '-o', GetoptLong::NO_ARGUMENT,       _("Resize images with optimized sizes for 3/2 aspect ratio rather than 4/3 (typical aspect ratio of pictures from non digital cameras are 3/2 when pictures from digital cameras are 4/3)") ],
@@ -193,6 +196,13 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") %
             when '--sizes'
                 $limit_sizes = arg
 
+            when '--multi-languages'
+                parts = arg.split(',')
+                if parts.size == 0 || parts.find_all { |e| ! SUPPORTED_LANGUAGES.include?(e.strip) }.size > 0
+                    die _("--multi-languages: argument must be a comma-separated list of supported languages, with last element used as the default language; for example: 'fr,eo,en,en'; supported languages: %s") % SUPPORTED_LANGUAGES.join(', ')
+                end
+                $multi_languages = [ parts[0..-2], parts[-1] ]
+
             when '--thumbnails-per-row'
                 $N_per_row = arg
 
@@ -243,6 +253,11 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") %
         if $mode == 'use_config' || $mode =~ /^merge_config/
             $optimize_for_32 = !$xmldoc.root.attributes['optimize-for-32'].nil?
             $N_per_row = $xmldoc.root.attributes['thumbnails-per-row']
+            languages = $xmldoc.root.attributes['multi-languages']
+            if languages
+                languages = languages.split(',')
+                $multi_languages = [ languages[0..-2], languages[-1] ]
+            end
             $N_per_page = $xmldoc.root.attributes['thumbnails-per-page']
             $madewith = $xmldoc.root.attributes['made-with']
             $indexlink = $xmldoc.root.attributes['index-link']
@@ -274,6 +289,9 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") %
         if $limit_sizes
             additional_params += "limit-sizes='#{$limit_sizes}'"
         end
+        if $multi_languages
+            additional_params += " multi-languages='#{$multi_languages[0].join(',')},#{$multi_languages[1]}'"
+        end
         if $optimize_for_32
             additional_params += " optimize-for-32='true'"
         end
@@ -302,6 +320,12 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") %
         else
             $xmldoc.root.delete_attribute('limit-sizes')
         end
+        if $multi_languages
+            $xmldoc.root.add_attribute('multi-languages', $multi_languages[0].join(',') + ',' + $multi_languages[1])
+        else
+            $xmldoc.root.delete_attribute('multi-languages')
+        end
+
         if $optimize_for_32
             $xmldoc.root.add_attribute('optimize-for-32', 'true')
         else
@@ -332,6 +356,11 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") %
     if $madewith
         $madewith = $madewith.gsub('%booh', '"http://booh.org/"')
     end
+    if $multi_languages
+        $htmlsuffix = ''
+    else
+        $htmlsuffix = '.html'
+    end
 end
 
 def read_config
@@ -418,11 +447,12 @@ def html_reload_to_thumbnails
     html_reload_to_thumbnails = $preferred_size_reloader.clone
     html_reload_to_thumbnails.gsub!(/~~theme~~/, $theme)
     html_reload_to_thumbnails.gsub!(/~~default_size~~/, $default_size['name'])
+    html_reload_to_thumbnails.gsub!(/~~htmlsuffix~~/, $htmlsuffix)
     html_reload_to_thumbnails.gsub!(/~~all_sizes~~/, all_images_sizes.collect { |s| "\"#{size2js(s['name'])}\"" }.join(', '))
     size_auto_chooser = '';
     all_images_sizes.find_all { |s| s.has_key?('optimizedforwidth') }.
                      sort { |a,b| b['optimizedforwidth'].to_i <=> a['optimizedforwidth'].to_i }.
-                     each { |s| size_auto_chooser += "if (w + 50 > #{s['optimizedforwidth']}) { return 'thumbnails-#{size2js(s['name'])}-0.html'; }\n" }
+                     each { |s| size_auto_chooser += "if (w + 50 > #{s['optimizedforwidth']}) { return 'thumbnails-#{size2js(s['name'])}-0#{$htmlsuffix}'; }\n" }
     html_reload_to_thumbnails.gsub!(/~~size_auto_chooser~~/, size_auto_chooser)
     return html_reload_to_thumbnails
 end
@@ -519,10 +549,10 @@ def substitute_html_sizes(html, sizeobj, type, suffix)
     if $images_size.length > 1 || (type == 'image' && $limit_sizes =~ /original/)
         for sizeobj2 in $images_size
             sizejs = size2js(sizeobj2['name'])
-            sizen = sizename(sizeobj2['name'])
+            sizen = defer_translation(sizename(sizeobj2['name'], false))
             if sizeobj != sizeobj2
                 if type == 'thumbnails'
-                    sizestrings << '<a href="thumbnails-' + sizejs + suffix + '.html" onclick="set_preferred_size(\'' + sizejs + '\')">' + sizen + '</a>'
+                    sizestrings << '<a href="thumbnails-' + sizejs + suffix + $htmlsuffix + '" onclick="set_preferred_size(\'' + sizejs + '\')">' + sizen + '</a>'
                 else
                     sizestrings << '<a id="link' + sizejs + '" onclick="set_preferred_size(\'' + sizejs + '\')">' + sizen + '</a>'
                 end
@@ -531,7 +561,7 @@ def substitute_html_sizes(html, sizeobj, type, suffix)
             end
         end
         if type == 'image' && $limit_sizes =~ /original/
-            sizestrings << '<a id="linkoriginal" target="newframe">' + sizename('original') + '</a>'
+            sizestrings << '<a id="linkoriginal" target="newframe">' + defer_translation(sizename('original'), false) + '</a>'
         end
     end
     html.sub!(/~~sizes~~(.+)~~/) { sizestrings.join($1) }
@@ -544,7 +574,7 @@ def substitute_navigation(html, xmldir)
         parent = xmldir.parent
         while parent.name == 'dir'
             parentcaption = parent.attributes['subdirs-caption'] || File.basename(parent.attributes['path'])
-            nav = "<a href=\"#{path}/index.html\">#{parentcaption}</a> #{utf8(_(" > "))} #{nav}"
+            nav = "<a href=\"#{path}/index#{$htmlsuffix}\">#{parentcaption}</a> #{defer_translation(N_(" > "))} #{nav}"
             path += '/..'
             parent = parent.parent
         end
@@ -626,16 +656,29 @@ def find_next_album(xmldir)
     return File.reduce_path(relative_pos)
 end
 
-def sub_previous_next_album(previous_album, next_album, html)
+def find_translation_for_file(file, msg)
+    if $multi_languages
+        if file =~ /\.(\w\w)\.html$/
+            bindtextdomain("booh", { :locale => "#{$1}.UTF-8" })
+            return _(msg)
+        else
+            die "Internal error: cannot find multi language suffix of file '#{file}'"
+        end
+    else
+        return utf8(_(msg))
+    end
+end
+
+def sub_previous_next_album(file, previous_album, next_album, html)
     if previous_album
-        html.gsub!(/~~previous_album~~/, '<a href="' + previous_album + 'thumbnails.html">' + utf8(_('previous album')) + '</a>')
+        html.gsub!(/~~previous_album~~/, '<a href="' + previous_album + 'thumbnails' + $htmlsuffix + '">' + find_translation_for_file(file, N_('previous album')) + '</a>')
         html.gsub!(/~~ifprevious_album\?~~(.+?)~~fi~~/) { $1 }
     else
         html.gsub!(/~~previous_album~~/, '')
         html.gsub!(/~~ifprevious_album\?~~(.+?)~~fi~~/, '')
     end
     if next_album
-        html.gsub!(/~~next_album~~/, '<a href="' + next_album + 'thumbnails.html">' + utf8(_('next album')) + '</a>')
+        html.gsub!(/~~next_album~~/, '<a href="' + next_album + 'thumbnails' + $htmlsuffix + '">' + find_translation_for_file(file, N_('next album')) + '</a>')
         html.gsub!(/~~ifnext_album\?~~(.+?)~~fi~~/) { $1 }
     else
         html.gsub!(/~~next_album~~/, '')
@@ -644,6 +687,24 @@ def sub_previous_next_album(previous_album, next_album, html)
     return html
 end
 
+def save_html(html, base_filename)
+    if html.class == Array
+        html = html.join("\n")
+    end
+    if $multi_languages
+        for language in $multi_languages[0]
+            bindtextdomain("booh", { :locale => "#{language}.UTF-8" })
+            ios = File.open("#{base_filename}.#{language}.html", "w")
+            ios.write(html.gsub(/@@(.*?)@@/) { _($1) })
+            ios.close
+        end
+    else
+        ios = File.open("#{base_filename}.html", "w")
+        ios.write(html.gsub(/@@(.*?)@@/) { utf8(_($1)) })
+        ios.close
+    end
+end
+
 def walk_source_dir
 
     #- preprocess the path->dir, rexml is very slow with that; we seem to improve speed by 7%
@@ -782,6 +843,11 @@ def walk_source_dir
                 msg 3, _("\tgenerating password protection file #{dest_dir}/.htaccess")
                 ios.write("AuthType Basic\nAuthName \"protected area\"\nAuthUserFile #{auth_user_file}\nrequire valid-user\n")
             end
+            if $multi_languages
+                ios.write("LanguagePriority #{$multi_languages[1]}\n")
+                ios.write("ForceLanguagePriority Prefer Fallback\n")
+                ios.write("DirectoryIndex index\n")
+            end
             ios.close
         end
 
@@ -1015,7 +1081,7 @@ def walk_source_dir
                                                 '<a href="' + videos[index] + '">' + img_element("#{dest_dir}/#{thumbnail_videos[sizeobj['name']][index]}") + '</a>')
                             else
                                 html_elem.gsub!(/~~image_iteration~~/,
-                                                '<a href="' + videos[index] + '">' + utf8(_("(no preview)")) + '</a>')
+                                                '<a href="' + videos[index] + '">' + defer_translation(N_("(no preview)")) + '</a>')
                             end
                             html_elem.gsub!(/~~caption_iteration~~/, find_caption_value(xmldir, videos[index]) || utf8(videos[index]))
                             html_elem.gsub!(/~~ifimage\?~~(.+?)~~fi~~/, '')
@@ -1025,7 +1091,7 @@ def walk_source_dir
                         html_thumbnails_nojs += html_elem
                         if type == 'image'
                             html_thumbnails.gsub!(/~~image_iteration~~/,
-                                                  '<a href="image-' + size2js(sizeobj['name']) + '.html#current=' + fullscreen_images[sizeobj['name']][index] +
+                                                  '<a href="image-' + size2js(sizeobj['name']) + $htmlsuffix + '#current=' + fullscreen_images[sizeobj['name']][index] +
                                                       '" name="' + fullscreen_images[sizeobj['name']][index] + '">' +
                                                       img_element("#{dest_dir}/#{thumbnail_images[sizeobj['name']][index]}") + '</a>')
                             html_thumbnails_nojs.gsub!(/~~image_iteration~~/, 
@@ -1057,6 +1123,7 @@ def walk_source_dir
                 for i in html
                     i.gsub!(/~~theme~~/, $theme)
                     i.gsub!(/~~current_size~~/, sizeobj['name'])
+                    i.gsub!(/~~htmlsuffix~~/, $htmlsuffix)
                     i.gsub!(/~~current_size_js~~/, size2js(sizeobj['name']))
                     i.gsub!(/~~madewith~~/, $madewith || '')
                     i.gsub!(/~~indexlink~~/, $indexlink || '')
@@ -1067,12 +1134,13 @@ def walk_source_dir
                 for page in all_pages
                     html_thumbnails, html_thumbnails_nojs = page
                     final_html = html.collect { |l| l.clone }
-                    mstuff = utf8(_("Pages: %s") % (pagecount > 0 ? "<a href=\"thumbnails-#{size2js(sizeobj['name'])}%nojs-#{pagecount - 1}.html\">" + _("<- Previous") + "</a> " : '') +
-                                                   all_pages.collect_with_index { |p,idx| page == p ? idx + 1 : "<a href=\"thumbnails-#{size2js(sizeobj['name'])}%nojs-#{idx}.html\">#{idx + 1}</a>" }.join(', ') +
-                                                   (pagecount < all_pages.size - 1 ? " <a href=\"thumbnails-#{size2js(sizeobj['name'])}%nojs-#{pagecount + 1}.html\">" + _("Next ->") + "</a> " : ''))
+                    mstuff = defer_translation(N_("Pages: ")) +
+                             (pagecount > 0 ? "<a href=\"thumbnails-#{size2js(sizeobj['name'])}%nojs-#{pagecount - 1}#{$htmlsuffix}\">" + defer_translation(N_("<- Previous")) + "</a> " : '') +
+                             all_pages.collect_with_index { |p,idx| page == p ? idx + 1 : "<a href=\"thumbnails-#{size2js(sizeobj['name'])}%nojs-#{idx}#{$htmlsuffix}\">#{idx + 1}</a>" }.join(', ') +
+                             (pagecount < all_pages.size - 1 ? " <a href=\"thumbnails-#{size2js(sizeobj['name'])}%nojs-#{pagecount + 1}#{$htmlsuffix}\">" + defer_translation(N_("Next ->")) + "</a> " : '')
                     for i in final_html
                         i.sub!(/~~run_slideshow~~/,
-                               images.size <= 1 ? '' : '<a href="image-' + size2js(sizeobj['name']) + '.html#run_slideshow=1">' + utf8(_("Run slideshow!"))+'</a>')
+                               images.size <= 1 ? '' : '<a href="image-' + size2js(sizeobj['name']) + $htmlsuffix + '#run_slideshow=1">' + defer_translation(N_("Run slideshow!"))+'</a>')
                         i.sub!(/~~thumbnails~~/, html_thumbnails)
                         if all_pages.size == 1
                             i.gsub!(/~~ifmultiplepages\?~~.*~~fi~~/, '')
@@ -1083,12 +1151,10 @@ def walk_source_dir
                         substitute_html_sizes(i, sizeobj, 'thumbnails', "-#{pagecount}")
                         substitute_pathtobase(i, xmldir)
                     end
-                    ios = File.open("#{dest_dir}/thumbnails-#{size2js(sizeobj['name'])}-#{pagecount}.html", "w")
-                    ios.write(final_html)
-                    ios.close
+                    save_html(final_html, "#{dest_dir}/thumbnails-#{size2js(sizeobj['name'])}-#{pagecount}")
                     final_html_nojs = html_nojs.collect { |l| l.clone }
                     for i in final_html_nojs
-                        i.sub!(/~~run_slideshow~~/, utf8(_("<i>Click on an image to view it larger</i>")))
+                        i.sub!(/~~run_slideshow~~/, defer_translation(N_("<i>Click on an image to view it larger</i>")))
                         i.sub!(/~~thumbnails~~/, html_thumbnails_nojs)
                         if all_pages.size == 1
                             i.gsub!(/~~ifmultiplepages\?~~.*~~fi~~/, '')
@@ -1098,9 +1164,7 @@ def walk_source_dir
                         end
                         substitute_html_sizes(i, sizeobj, 'thumbnails', "-nojs-#{pagecount}")
                     end
-                    ios = File.open("#{dest_dir}/thumbnails-#{size2js(sizeobj['name'])}-nojs-#{pagecount}.html", "w")
-                    ios.write(final_html_nojs)
-                    ios.close
+                    save_html(final_html_nojs, "#{dest_dir}/thumbnails-#{size2js(sizeobj['name'])}-nojs-#{pagecount}")
                     pagecount += 1
                 end
             end
@@ -1108,9 +1172,7 @@ def walk_source_dir
             info("finished processing sizes")
 
             #- generate "main" thumbnails.html page that will reload to correct size thanks to cookie
-            ios = File.open("#{dest_dir}/thumbnails.html", "w")
-            ios.write(html_reload_to_thumbnails)
-            ios.close
+            save_html(html_reload_to_thumbnails, "#{dest_dir}/thumbnails")
 
             #- generate image.html (page with fullscreen images)
             if images.size > 0
@@ -1134,18 +1196,17 @@ def walk_source_dir
                         i.gsub!(/~~other_sizes~~/, othersizes.join(', '))
                         i.gsub!(/~~captions~~/, captions4js)
                         i.gsub!(/~~title~~/, xmldir.attributes['thumbnails-caption'] || utf8(File.basename(dir)))
-                        i.gsub!(/~~thumbnails~~/, '<a href="thumbnails-' + size2js(sizeobj['name']) + '.html" id="thumbnails">' + utf8(_('return to thumbnails')) + '</a>')
+                        i.gsub!(/~~thumbnails~~/, '<a href="thumbnails-' + size2js(sizeobj['name']) + $htmlsuffix + '" id="thumbnails">' + defer_translation(N_('return to thumbnails')) + '</a>')
                         i.gsub!(/~~theme~~/, $theme)
                         i.gsub!(/~~current_size~~/, size2js(sizeobj['name']))
+                        i.gsub!(/~~htmlsuffix~~/, $htmlsuffix)
                         i.gsub!(/~~madewith~~/, $madewith || '')
                         i.gsub!(/~~indexlink~~/, $indexlink || '')
                         substitute_html_sizes(i, sizeobj, 'image', '')
                         substitute_navigation(i, xmldir)
                         substitute_pathtobase(i, xmldir)
                     end
-                    ios = File.open("#{dest_dir}/image-#{size2js(sizeobj['name'])}.html", "w")
-                    ios.write(html)
-                    ios.close
+                    save_html(html, "#{dest_dir}/image-#{size2js(sizeobj['name'])}")
                 end
             end
         end
@@ -1239,7 +1300,7 @@ def walk_source_dir
                 gen_thumbnails_subdir(from_utf8(xmldir.attributes['thumbnails-captionfile']), xmldir, false,
                                       [ { 'filename' => thumbnail, 'size' => $albums_thumbnail_size } ], 'thumbnails')
                 html_index += run_iterations(iterations, 1)
-                html_index.gsub!(/~~image_iteration~~/, "<a href=\"thumbnails.html\">" + img_element(thumbnail) + '</a>')
+                html_index.gsub!(/~~image_iteration~~/, "<a href=\"thumbnails#{$htmlsuffix}\">" + img_element(thumbnail) + '</a>')
                 html_index.gsub!(/~~caption_iteration~~/, xmldir.attributes['thumbnails-caption'])
             end
 
@@ -1255,7 +1316,7 @@ def walk_source_dir
                 gen_thumbnails_subdir(captionfile, child, false,
                                       [ { 'filename' => thumbnail, 'size' => $albums_thumbnail_size } ], find_subalbum_info_type(child))
                 html_index.gsub!(/~~caption_iteration~~/, caption)
-                html_index.gsub!(/~~image_iteration~~/, "<a href=\"#{subdir}/index.html\">" + img_element(thumbnail) + '</a>')
+                html_index.gsub!(/~~image_iteration~~/, "<a href=\"#{subdir}/index#{$htmlsuffix}\">" + img_element(thumbnail) + '</a>')
             }
 
             html_index += close_iterations(iterations)
@@ -1270,9 +1331,13 @@ def walk_source_dir
             html = html_reload_to_thumbnails
         end
 
-        ios = File.open("#{dest_dir}/index.html", "w")
-        ios.write(html)
-        ios.close
+        save_html(html, "#{dest_dir}/index")
+        if $multi_languages
+            #- in case MultiViews will not work, generate some compat
+            ios = File.open("#{dest_dir}/index.html", "w")
+            ios.write("<html><head><meta http-equiv=\"refresh\" content=\"0.1;url=index.#{$multi_languages[1]}.html\"></head></html>")
+            ios.close
+        end
 
         #- substitute multiple "return to albums", previous/next correctly
         #- the following two statements are dramatical optimizations to executing for each substInFile callback
@@ -1283,21 +1348,23 @@ def walk_source_dir
                 for sizeobj in $images_size
                     Dir.glob("#{dest_dir}/thumbnails-#{size2js(sizeobj['name'])}#{suffix}-*.html") do |file|
                         substInFile(file) { |line|
-                            sub_previous_next_album(previous_album, next_album, line)
+                            sub_previous_next_album(file, previous_album, next_album, line)
                             if dirpresent
-                                line.sub!(/~~return_to_albums~~/, '<a href="index.html">' + utf8(_('return to albums')) + '</a>')
+                                line.sub!(/~~return_to_albums~~/, '<a href="index' + $htmlsuffix + '">' + find_translation_for_file(file, N_('return to albums')) + '</a>')
                             else
                                 if parentname == 'dir'
-                                    line.sub!(/~~return_to_albums~~/, '<a href="../index.html">' + utf8(_('return to albums')) + '</a>')
+                                    line.sub!(/~~return_to_albums~~/, '<a href="../index' + $htmlsuffix + '">' + find_translation_for_file(file, N_('return to albums')) + '</a>')
                                 else
                                     line.sub!(/~~return_to_albums~~/, '')
                                 end
                             end
                             line
                         }
-                        if suffix == '' && xmldir.child_byname_notattr('image', 'deleted')
-                            substInFile("#{dest_dir}/image-#{size2js(sizeobj['name'])}.html") { |line|
-                                sub_previous_next_album(previous_album, next_album, line)
+                    end
+                    if suffix == '' && xmldir.child_byname_notattr('image', 'deleted')
+                        Dir.glob("#{dest_dir}/image-#{size2js(sizeobj['name'])}*.html") do |file|
+                            substInFile(file) { |line|
+                                sub_previous_next_album(file, previous_album, next_album, line)
                             }
                         end
                     end