summaryrefslogtreecommitdiff
path: root/scripts/code_tools
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/code_tools')
-rw-r--r--scripts/code_tools/modified-strings.sh199
-rw-r--r--scripts/code_tools/modified_strings.py211
2 files changed, 410 insertions, 0 deletions
diff --git a/scripts/code_tools/modified-strings.sh b/scripts/code_tools/modified-strings.sh
new file mode 100644
index 0000000000..435dda3f5d
--- /dev/null
+++ b/scripts/code_tools/modified-strings.sh
@@ -0,0 +1,199 @@
+#!/usr/bin/env bash
+# $LicenseInfo:firstyear=2014&license=viewerlgpl$
+# Second Life Viewer Source Code
+# Copyright (C) 2011, Linden Research, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation;
+# version 2.1 of the License only.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+#
+###
+### Extract strings modified between some version and the current version
+###
+
+Action=DEFAULT
+Rev=master
+DefaultXuiDir="indra/newview/skins/default/xui"
+Verbose=false
+ExitStatus=0
+
+while [ $# -ne 0 ]
+do
+ case ${1} in
+ ##
+ ## Show usage
+ ##
+ -h|--help)
+ Action=USAGE
+ ;;
+
+ -v|--verbose)
+ Verbose=true
+ ;;
+
+ ##
+ ## Select the revision to compare against
+ ##
+ -r)
+ if [ $# -lt 2 ]
+ then
+ echo "Must specify <revision> with ${1}" 1>&2
+ Action=USAGE
+ ExitStatus=1
+ break
+ else
+ Rev=${2}
+ shift # consume the switch ( for n values, consume n-1 )
+ fi
+ ;;
+
+ ##
+ ## handle an unknown switch
+ ##
+ -*)
+ Action=USAGE
+ ExitStatus=1
+ break
+ ;;
+
+ *)
+ if [ -z "${XuiDir}" ]
+ then
+ XuiDir=${1}
+ else
+ echo "Too many arguments supplied: $@" 1>&2
+ Action=USAGE
+ ExitStatus=1
+ break
+ fi
+ ;;
+ esac
+
+ shift # always consume 1
+done
+
+progress()
+{
+ if $Verbose
+ then
+ echo $* 1>&2
+ fi
+}
+
+if [[ $ExitStatus -eq 0 && "${Action}" = "DEFAULT" ]]
+then
+ if [[ ! -d "${XuiDir:=$DefaultXuiDir}" ]]
+ then
+ echo "No XUI directory found in '$XuiDir'" 1>&2
+ Action=USAGE
+ ExitStatus=1
+ fi
+fi
+
+if [ "${Action}" = "USAGE" ]
+then
+ cat <<USAGE
+
+Usage:
+
+ modified-strings.sh [ { -v | --verbose } ] [-r <revision>] [<path-to-xui>]
+
+ where
+ --verbose shows progress messages on stderr (the command takes a while, so this is reassuring)
+
+ -r <revision> specifies a git revision (branch, tag, commit, or relative specifier)
+ defaults to 'master' so that comparison is against the HEAD of the released viewer branch
+
+ <path-to-xui> is the path to the root directory for XUI files
+ defaults to '$DefaultXuiDir'
+
+ Emits a tab-separated file with these columns:
+ filename
+ the path of a file that has a string change (columns 2 and 3 are empty for lines with a filename)
+ name
+ the name attribute of a string or label whose value changed
+ English value
+ the current value of the string or label whose value changed
+ for strings, newlines are changed to '\n' and tab characters are changed to '\t'
+
+ There is also a column for each of the language directories following the English.
+
+USAGE
+ exit $ExitStatus
+fi
+
+stringval() # reads stdin and prints the escaped value of a string for the requested tag
+{
+ local tag=$1
+ xmllint --xpath "string(/strings/string[@name=\"$tag\"])" - | perl -p -e 'chomp; s/\n/\\n/g; s/\t/\\t/g;'
+}
+
+columns="file\tname\tEN"
+for lang in $(ls -1 ${XuiDir})
+do
+ if [[ "$lang" != "en" && -d "${XuiDir}" && -f "${XuiDir}/$lang/strings.xml" ]]
+ then
+ columns+="\t$lang"
+ fi
+done
+echo -e "$columns"
+
+EnglishStrings="${XuiDir}/en/strings.xml"
+progress -n "scanning $EnglishStrings "
+echo -e "$EnglishStrings"
+# loop over all tags in the current version of the strings file
+cat "$EnglishStrings" | xmllint --xpath '/strings/string/@name' - | sed 's/ name="//; s/"$//;' \
+| while read name
+do
+ progress -n "."
+ # fetch the $Rev and current values for each tag
+ old_stringval=$(git show "$Rev:$EnglishStrings" 2> /dev/null | stringval "$name")
+ new_stringval=$(cat "$EnglishStrings" | stringval "$name")
+
+ if [[ "$old_stringval" != "$new_stringval" ]]
+ then
+ # the value is different, so print the tag and it's current value separated by a tab
+ echo -e "\t$name\t$new_stringval"
+ fi
+done
+progress ""
+
+# loop over all XUI files other than strings.xml finding labels
+grep -rlw 'label' "${XuiDir}/en" | grep -v '/strings.xml' \
+| while read xuipath
+do
+ progress -n "scanning $xuipath "
+ listed_file=false
+ # loop over all elements for which there is a label attribute, getting the name attribute value
+ xmllint --xpath '//*[@label]/@name' "$xuipath" 2> /dev/null | sed 's/ name="//; s/"$//;' \
+ | while read name
+ do
+ progress -n "."
+ # get the old and new label attribute values for each name
+ old_label=$(git show "$Rev:$xuipath" 2> /dev/null | xmllint --xpath "string(//*[@name=\"${name}\"]/@label)" - 2> /dev/null)
+ new_label=$(cat "$xuipath" | xmllint --xpath "string(//*[@name=\"${name}\"]/@label)" - 2> /dev/null)
+ if [[ "$old_label" != "$new_label" ]]
+ then
+ if ! $listed_file
+ then
+ echo -e "$xuipath"
+ listed_file=true
+ fi
+ echo -e "\t$name\t$new_label"
+ fi
+ done
+ progress ""
+done
+
diff --git a/scripts/code_tools/modified_strings.py b/scripts/code_tools/modified_strings.py
new file mode 100644
index 0000000000..77954637a4
--- /dev/null
+++ b/scripts/code_tools/modified_strings.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python
+"""\
+
+This script scans the SL codebase for translation-related strings.
+
+$LicenseInfo:firstyear=2020&license=viewerlgpl$
+Second Life Viewer Source Code
+Copyright (C) 2020, Linden Research, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation;
+version 2.1 of the License only.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+$/LicenseInfo$
+"""
+
+from __future__ import print_function
+
+import xml.etree.ElementTree as ET
+import argparse
+import os
+import sys
+from git import Repo, Git # requires the gitpython package
+import pandas as pd
+
+translate_attribs = [
+ "title",
+ "short_title",
+ "value",
+ "label",
+ "label_selected",
+ "tool_tip",
+ "ignoretext",
+ "yestext",
+ "notext",
+ "canceltext",
+ "description",
+ "longdescription"
+]
+
+def codify_for_print(val):
+ if isinstance(val, unicode):
+ return val.encode("utf-8")
+ else:
+ return unicode(val, 'utf-8').encode("utf-8")
+
+# Returns a dict of { name => xml_node }
+def read_xml_elements(blob):
+ try:
+ contents = blob.data_stream.read()
+ except:
+ # default - pretend we read a file with no elements of interest.
+ # Parser will complain if it gets no elements at all.
+ contents = '<?xml version="1.0" encoding="utf-8" standalone="yes" ?><strings></strings>'
+ xml = ET.fromstring(contents)
+ elts = {}
+ for child in xml.iter():
+ if "name" in child.attrib:
+ name = child.attrib['name']
+ elts[name] = child
+ return elts
+
+def failure(*msg):
+ print(*msg)
+ sys.exit(1)
+
+def can_translate(val):
+ if val is None:
+ return False
+ if val.isspace():
+ return False
+ val = val.strip()
+ if val.isdigit():
+ return False
+ return True
+
+usage_msg="""%(prog)s [options]
+
+Analyze the XUI configuration files to find text that may need to
+be translated. Works by comparing two specified revisions, one
+specified by --rev (default HEAD) and one specified by --rev_base
+(default master). The script works by comparing xui contents of the
+two revisions, and outputs a spreadsheet listing any areas of
+difference. The target language must be specified using the --lang
+option. Output is an excel file, which can be used as-is or imported
+into google sheets.
+
+If the --rev revision already contains a translation for the text, it
+will be included in the spreadsheet for reference.
+
+Normally you would want --rev_base to be the last revision to have
+translations added, and --rev to be the tip of the current
+project.
+
+"""
+
+if __name__ == "__main__":
+
+ parser = argparse.ArgumentParser(description="analyze viewer xui files for needed translations", usage=usage_msg)
+ parser.add_argument("-v","--verbose", action="store_true", help="verbose flag")
+ parser.add_argument("--rev", help="revision with modified strings, default HEAD", default="HEAD")
+ parser.add_argument("--rev_base", help="previous revision to compare against, default master", default="master")
+ parser.add_argument("--base_lang", help="base language, default en (useful only for testing)", default="en")
+ parser.add_argument("--lang", help="target language")
+ args = parser.parse_args()
+
+ cwd = os.getcwd()
+ rootdir = Git(cwd).rev_parse("--show-toplevel")
+ repo = Repo(rootdir)
+ try:
+ mod_commit = repo.commit(args.rev)
+ except:
+ failure(args.rev,"is not a valid commit")
+ try:
+ base_commit = repo.commit(args.rev_base)
+ except:
+ failure(args.rev_base,"is not a valid commit")
+
+ mod_tree = mod_commit.tree
+ base_tree = base_commit.tree
+
+ xui_base = "indra/newview/skins/default/xui"
+ xui_base_tree = mod_tree[xui_base]
+ valid_langs = [tree.name.lower() for tree in xui_base_tree if tree.name.lower() != args.base_lang.lower()]
+ if not args.lang or not args.lang.lower() in valid_langs:
+ failure("Please specify a target language using --lang. Valid values are", ",".join(sorted(valid_langs)))
+
+ xui_path = "{}/{}".format(xui_base, args.base_lang)
+ try:
+ mod_xui_tree = mod_tree[xui_path]
+ except:
+ failure("xui tree not found for base language", args.base_lang)
+
+ if args.rev == args.rev_base:
+ failure("Revs are the same, nothing to compare")
+
+ print("Finding changes in", args.rev, "not present in", args.rev_base)
+ sys.stdout.flush()
+
+ data = []
+ # For all files to be checked for translations
+ for mod_blob in mod_xui_tree.traverse():
+ filename = mod_blob.path
+ if mod_blob.type == "tree": # directory, skip
+ continue
+
+ if args.verbose:
+ print(filename)
+
+ try:
+ base_blob = base_tree[filename]
+ except:
+ if args.verbose:
+ print("No matching base file found for", filename)
+ base_blob = None
+
+ try:
+ transl_filename = filename.replace("/xui/{}/".format(args.base_lang), "/xui/{}/".format(args.lang))
+ transl_blob = mod_tree[transl_filename]
+ except:
+ if args.verbose:
+ failure("No matching translation file found at", transl_filename)
+ transl_blob = None
+
+ mod_dict = read_xml_elements(mod_blob)
+ base_dict = read_xml_elements(base_blob)
+ transl_dict = read_xml_elements(transl_blob)
+
+ rows = 0
+ for name in mod_dict.keys():
+ if not name in base_dict or mod_dict[name].text != base_dict[name].text:
+ val = mod_dict[name].text
+ if can_translate(val):
+ transl_val = "--"
+ if name in transl_dict:
+ transl_val = transl_dict[name].text
+ data.append([filename, name, "text", val, transl_val, ""])
+ rows += 1
+ for attr in translate_attribs:
+ if attr in mod_dict[name].attrib:
+ if name not in base_dict or attr not in base_dict[name].attrib or mod_dict[name].attrib[attr] != base_dict[name].attrib[attr]:
+ val = mod_dict[name].attrib[attr]
+ if can_translate(val):
+ transl_val = "--"
+ if name in transl_dict and attr in transl_dict[name].attrib:
+ transl_val = transl_dict[name].attrib[attr]
+ data.append([filename, name, attr, val, transl_val, ""])
+ rows += 1
+ if args.verbose and rows>0:
+ print(" ",rows,"rows added")
+
+ outfile = "SL_Translations_{}.xlsx".format(args.lang.upper())
+ cols = ["File", "Element", "Field", "EN", "Previous Translation ({})".format(args.lang.upper()), "ENTER NEW TRANSLATION ({})".format(args.lang.upper())]
+ num_translations = len(data)
+ df = pd.DataFrame(data, columns=cols)
+ df.to_excel(outfile, index=False)
+ if num_translations>0:
+ print("Wrote", num_translations, "rows to file", outfile)
+ else:
+ print("Nothing to translate,", outfile, "is empty")