blob: e5d040ff0054617e7d2ddffe9f28db46b8e4155f [file] [log] [blame] [edit]
#!/usr/bin/env tclsh
###############################################################################
# BRLTTY - A background process providing access to the console screen (when in
# text mode) for a blind person using a refreshable braille display.
#
# Copyright (C) 1995-2023 by The BRLTTY Developers.
#
# BRLTTY comes with ABSOLUTELY NO WARRANTY.
#
# This is free software, placed under the terms of the
# GNU Lesser General Public License, as published by the Free Software
# Foundation; either version 2.1 of the License, or (at your option) any
# later version. Please see the file LICENSE-LGPL for details.
#
# Web Page: http://brltty.app/
#
# This software is maintained by Dave Mielke <dave@mielke.cc>.
###############################################################################
source [file join [file dirname [info script]] "prologue.tcl"]
proc splitNames {names oldNamesVariable sameNamesVariable newNamesVariable} {
global baseStrings ignorableNames
upvar 1 $oldNamesVariable oldNames
upvar 1 $sameNamesVariable sameNames
upvar 1 $newNamesVariable newNames
lassign [intersect3 $names [dict keys $baseStrings]] oldNames sameNames newNames
lassign [intersect3 $newNames $ignorableNames] newNames x x
}
proc writeNames {path names} {
global baseStrings
file delete $path
if {[llength $names] > 0} {
if {[catch [list open $path {WRONLY TRUNC CREAT}] channel] == 0} {
foreach name $names {
set string [dict get $baseStrings $name]
puts $channel "$name [dict get $string text]"
}
close $channel; unset channel
} else {
semanticError $channel
}
}
}
proc loadStringList {} {
global androidDirectory
set strings [dict create]
set path [file join $androidDirectory STRINGS]
logMessage detail "loading string list: $path"
set order 0
set pattern {^\s*(\S+)\s+(.*?)\s*$}
if {[catch [list open $path {RDONLY}] channel] == 0} {
while {[gets $channel line] >= 0} {
if {[regexp $pattern $line x name text]} {
set string [dict create text $text order [incr order]]
dict set strings $name $string
}
}
close $channel; unset channel
} else {
semanticError $channel
}
return $strings
}
proc auditStringList {} {
global baseStrings listedStrings
logNote "auditing string list"
splitNames [dict keys $listedStrings] oldNames sameNames newNames
writeNames "[getProgramName].txt" $newNames
foreach name $oldNames {
writeProgramMessage "old listed string: $name"
}
foreach name $sameNames {
set oldText [dict get $listedStrings $name text]
set newText [dict get $baseStrings $name text]
if {![string equal $newText $oldText]} {
writeProgramMessage "base string changed: $name: $oldText -> $newText"
}
}
}
proc loadStringResources {directory} {
logMessage detail "loading resource directory: $directory"
set strings [dict create]
set pattern {^\s*<\s*string}
append pattern {\s+name\s*=\s*"([^"]*)"}
append pattern {(?:\s+translatable\s*=\s*"true")?}
append pattern {(?:\s+tools:ignore\s*=\s*"(MissingTranslation)")?}
append pattern {\s*>\s*(.*?)\s*<\s*/string\s*>\s*$}
foreach path [glob "$directory/*.xml"] {
logMessage detail "loading string resources: $path"
if {[catch [list open $path {RDONLY}] channel] == 0} {
set order 0
while {[gets $channel line] >= 0} {
if {[regexp $pattern $line x name ignorable text]} {
set ignorable [expr {[string length $ignorable] > 0}]
set string [dict create text $text ignorable $ignorable order [incr order]]
dict set strings $name $string
}
}
close $channel; unset channel
} else {
semanticError $channel
}
}
return $strings
}
proc getIgnorableNames {strings} {
set names [list]
foreach name [dict keys $strings] {
if {[dict get $strings $name ignorable]} {
lappend names $name
}
}
return $names
}
proc auditLanguage {code directory} {
logNote "auditing language: $code"
set languageStrings [loadStringResources $directory]
splitNames [dict keys $languageStrings] oldNames sameNames newNames
writeNames "[getProgramName]-$code.txt" $newNames
foreach name $oldNames {
writeProgramMessage "old language string: $code: $name"
}
if {([string length $oldNames] + [string length $newNames]) == 0} {
global listedStrings
foreach name [dict keys $listedStrings] {
if {[dict get $listedStrings $name order] != [dict get $languageStrings $name order]} {
writeProgramMessage "language string out of order: $code: $name"
}
}
}
}
proc auditAllLanguages {} {
global baseDirectory
set languageDelimiter -
foreach directory [glob -nocomplain -path "$baseDirectory$languageDelimiter" "?*"] {
set code [lindex [split [file tail $directory] $languageDelimiter] 1]
auditLanguage $code $directory
}
}
set optionDefinitions {
}
processProgramArguments optionValues $optionDefinitions
set baseDirectory [file join $applicationDirectory res values]
set baseStrings [loadStringResources $baseDirectory]
set ignorableNames [getIgnorableNames $baseStrings]
set listedStrings [loadStringList]
auditStringList
auditAllLanguages
exit 0