- I OpenACS For Everyone
- I.1 High level information: What is OpenACS?
- I.1.1 Overview
- I.1.2 OpenACS Release Notes
- I.2 OpenACS: robust web development framework
- I.2.1 Introduction
- I.2.2 Basic infrastructure
- I.2.3 Advanced infrastructure
- I.2.4 Domain level tools
- I.1 High level information: What is OpenACS?
- II Administrator's Guide
- II.2 Installation Overview
- II.2.1 Basic Steps
- II.2.2 Prerequisite Software
- II.3 Complete Installation
- II.3.1 Install a Unix-like system and supporting software
- II.3.2 Install Oracle 10g XE on debian
- II.3.2.1 Install Oracle 8.1.7
- II.3.3 Install PostgreSQL
- II.3.4 Install AOLserver 4
- II.3.5 Quick Install of OpenACS
- II.3.5.1 Complex Install OpenACS 5.3
- II.3.6 OpenACS Installation Guide for Windows2000
- II.3.7 OpenACS Installation Guide for Mac OS X
- II.4 Configuring a new OpenACS Site
- II.4.1 Installing OpenACS packages
- II.4.2 Mounting OpenACS packages
- II.4.3 Configuring an OpenACS package
- II.4.4 Setting Permissions on an OpenACS package
- II.4.5 How Do I?
- II.4.6 Configure OpenACS look and feel with templates
- II.5 Upgrading
- II.5.1 Overview
- II.5.2 Upgrading 4.5 or higher to 4.6.3
- II.5.3 Upgrading OpenACS 4.6.3 to 5.0
- II.5.4 Upgrading an OpenACS 5.0.0 or greater installation
- II.5.5 Upgrading the OpenACS files
- II.5.6 Upgrading Platform components
- II.6 Production Environments
- II.6.1 Starting and Stopping an OpenACS instance.
- II.6.2 AOLserver keepalive with inittab
- II.6.3 Running multiple services on one machine
- II.6.4 High Availability/High Performance Configurations
- II.6.5 Staged Deployment for Production Networks
- II.6.6 Installing SSL Support for an OpenACS service
- II.6.7 Set up Log Analysis Reports
- II.6.8 External uptime validation
- II.6.9 Diagnosing Performance Problems
- II.7 Database Management
- II.7.1 Running a PostgreSQL database on another server
- II.7.2 Deleting a tablespace
- II.7.3 Vacuum Postgres nightly
- II.8 Backup and Recovery
- II.8.1 Backup Strategy
- II.8.2 Manual backup and recovery
- II.8.3 Automated Backup
- II.8.4 Using CVS for backup-recovery
- II.A Install Red Hat 8/9
- II.B Install additional supporting software
- II.B.1 Unpack the OpenACS tarball
- II.B.2 Initialize CVS (OPTIONAL)
- II.B.3 Add PSGML commands to emacs init file (OPTIONAL)
- II.B.4 Install Daemontools (OPTIONAL)
- II.B.5 Install qmail (OPTIONAL)
- II.B.6 Install Analog web file analyzer
- II.B.7 Install nspam
- II.B.8 Install Full Text Search
- II.B.9 Install Full Text Search using Tsearch2
- II.B.10 Install Full Text Search using OpenFTS (deprecated see tsearch2)
- II.B.11 Install nsopenssl
- II.B.12 Install tclwebtest.
- II.B.13 Install PHP for use in AOLserver
- II.B.14 Install Squirrelmail for use as a webmail system for OpenACS
- II.B.15 Install PAM Radius for use as external authentication
- II.B.16 Install LDAP for use as external authentication
- II.B.17 Install AOLserver 3.3oacs1
- II.C Credits
- II.C.1 Where did this document come from?
- II.C.2 Linux Install Guides
- II.C.3 Security Information
- II.C.4 Resources
- II.2 Installation Overview
- III For OpenACS Package Developers
- III.9 Development Tutorial
- III.9.1 Creating an Application Package
- III.9.2 Setting Up Database Objects
- III.9.3 Creating Web Pages
- III.9.4 Debugging and Automated Testing
- III.10 Advanced Topics
- III.10.1 Write the Requirements and Design Specs
- III.10.2 Add the new package to CVS
- III.10.3 OpenACS Edit This Page Templates
- III.10.4 Adding Comments
- III.10.5 Admin Pages
- III.10.6 Categories
- III.10.7 Profile your code
- III.10.8 Prepare the package for distribution.
- III.10.9 Distributing upgrades of your package
- III.10.10 Notifications
- III.10.11 Hierarchical data
- III.10.12 Using .vuh files for pretty urls
- III.10.13 Laying out a page with CSS instead of tables
- III.10.14 Sending HTML email from your application
- III.10.15 Basic Caching
- III.10.16 Scheduled Procedures
- III.10.17 Enabling WYSIWYG
- III.10.18 Adding in parameters for your package
- III.10.19 Writing upgrade scripts
- III.10.20 Connect to a second database
- III.10.21 Future Topics
- III.11 Development Reference
- III.11.1 OpenACS Packages
- III.11.2 OpenACS Data Models and the Object System
- III.11.3 The Request Processor
- III.11.4 The OpenACS Database Access API
- III.11.5 Using Templates in OpenACS
- III.11.6 Groups, Context, Permissions
- III.11.7 Writing OpenACS Application Pages
- III.11.8 Parties in OpenACS
- III.11.9 OpenACS Permissions Tediously Explained
- III.11.10 Object Identity
- III.11.11 Programming with AOLserver
- III.11.12 Using Form Builder: building html forms dynamically
- III.12 Engineering Standards
- III.12.1 OpenACS Style Guide
- III.12.2 Release Version Numbering
- III.12.3 Constraint naming standard
- III.12.4 ACS File Naming and Formatting Standards
- III.12.5 PL/SQL Standards
- III.12.6 Variables
- III.12.7 Automated Testing
- III.13 CVS Guidelines
- III.13.1 Using CVS with OpenACS
- III.13.2 OpenACS CVS Concepts
- III.13.3 Contributing code back to OpenACS
- III.13.4 Additional Resources for CVS
- III.14 Documentation Standards
- III.14.1 OpenACS Documentation Guide
- III.14.2 Using PSGML mode in Emacs
- III.14.3 Using nXML mode in Emacs
- III.14.4 Detailed Design Documentation Template
- III.14.5 System/Application Requirements Template
- III.15 TCLWebtest
- III.16 Internationalization
- III.16.1 Internationalization and Localization Overview
- III.16.2 How Internationalization/Localization works in OpenACS
- III.16.4 Design Notes
- III.16.5 Translator's Guide
- III.D Using CVS with an OpenACS Site
- III.9 Development Tutorial
- IV For OpenACS Platform Developers
- IV.17 Kernel Documentation
- IV.17.1 Overview
- IV.17.2 Object Model Requirements
- IV.17.3 Object Model Design
- IV.17.4 Permissions Requirements
- IV.17.5 Permissions Design
- IV.17.6 Groups Requirements
- IV.17.7 Groups Design
- IV.17.8 Subsites Requirements
- IV.17.9 Subsites Design Document
- IV.17.10 Package Manager Requirements
- IV.17.11 Package Manager Design
- IV.17.12 Database Access API
- IV.17.13 OpenACS Internationalization Requirements
- IV.17.14 Security Requirements
- IV.17.15 Security Design
- IV.17.16 Security Notes
- IV.17.17 Request Processor Requirements
- IV.17.18 Request Processor Design
- IV.17.19 Documenting Tcl Files: Page Contracts and Libraries
- IV.17.20 Bootstrapping OpenACS
- IV.17.21 External Authentication Requirements
- IV.18 Releasing OpenACS
- IV.18.1 OpenACS Core and .LRN
- IV.18.2 How to Update the OpenACS.org repository
- IV.18.3 How to package and release an OpenACS Package
- IV.18.4 How to Update the translations
- IV.17 Kernel Documentation
- V Tcl for Web Nerds
- V.1 Tcl for Web Nerds Introduction
- V.2 Basic String Operations
- V.3 List Operations
- V.4 Pattern matching
- V.5 Array Operations
- V.6 Numbers
- V.7 Control Structure
- V.8 Scope, Upvar and Uplevel
- V.9 File Operations
- V.10 Eval
- V.11 Exec
- V.12 Tcl for Web Use
- V.13 OpenACS conventions for TCL
- V.14 Solutions
- VI SQL for Web Nerds
- VI.1 SQL Tutorial
- VI.1.1 SQL Tutorial
- VI.1.2 Answers
- VI.2 SQL for Web Nerds Introduction
- VI.3 Data modeling
- VI.3.1 The Discussion Forum -- philg's personal odyssey
- VI.3.2 Data Types (Oracle)
- VI.3.4 Tables
- VI.3.5 Constraints
- VI.4 Simple queries
- VI.5 More complex queries
- VI.6 Transactions
- VI.7 Triggers
- VI.8 Views
- VI.9 Style
- VI.10 Escaping to the procedural world
- VI.11 Trees
- VI.1 SQL Tutorial
III.16.3 How to Internationalize a Package
Tip
For multilingual websites we recommend using the UTF8 charset. In order for AOLserver to use utf8 you need to set the config parameters OutputCharset and URLCharset to utf-8 in your AOLserver config file (use the etc/config.tcl template file). This is the default for OpenACS 5.1 and later. For sites running on Oracle you need to make sure that AOLserver is running with the NLS_LANG environment variable set to .UTF8. You should set this variable in the nsd-oracle run script (use the acs-core-docs/www/files/nds-oracle.txt template file).
-
Replace all text with temporary message tags.From
/acs-admin/apm/
, select a package and then click onInternationalization
, thenConvert ADP, Tcl, and SQL files to using the message catalog.
. This pass only changes the adp files; it does not affect catalog files or the catalog in the database.You will now be walked through all of the selected adp pages. The UI shows you the intended changes and lets you edit or cancel them key by key.
-
Replace the temporary message tags in ADP files.From the same
Convert ADP ...
page in/acs-admin/apm
as in the last step, repeat the process but deselectFind human language text ...
and selectReplace <# ... #> tags ...
and click OK. This step replaces all of the temporary tags with "short" message lookups, inserts the message keys into the database message catalog, and then writes that catalog out to an xml file. -
Replace human-readable text in TCL files with temporary tags.Examine all of the tcl files in the packages for human-readable text and replace it with temporary tags. The temporary tags in TCL are slightly different from those in ADP. If the first character in the temporary tag is an underscore (
_
), then the message keys will be auto-generated from the original message text. Here is an unmodified tcl file:set title "Messages for $a(name) in $b(label)" set context [list [list . "SimPlay"] [list [export_vars -base case-admin { case_id }] "Administer $a(name)"] "Messages for $a(name)"]
... and here is the same file after temporary message tags have been manually added:
set title <#admin_title Messages for %a.name% in %b.label%#> set context [list [list . <#_ SimPlay#>] [list [export_vars -base case-admin { case_id }] <#_ Administer %a.name%#>] <#_ Messages for %a.name%#>]
Note that the message key
case_admin_page_title
was manually selected, because an autogenerated key for this text, with its substitute variables, would have been very confusing -
Replace the temporary message tags in TCL files.Repeat step 2 for tcl files. Here is the example TCL file after conversion:
set title [_ simulation.admin_title] set context [list [list . [_ simulation.SimPlay]] [list [export_vars -base case-admin { case_id }] [_ simulation.lt_Administer_name_gt]] [_ simulation.lt_Messages_for_role_pre]]
-
Internationalize SQL Code.If there is any user-visible TCL code in the .sql or .xql files, internationalize that the same way as for the TCL files.
-
Internationalize Package Parameters. See Multilingual APM Parameters
-
Internationalize Date and Time queries.
-
Find datetime in .xql files. Use command line tools to find suspect SQL code:
grep -r "to_char.*H" * grep -r "to_date.*H" *
-
In SQL statements, replace the format string with the ANSI standard format,
YYYY-MM-DD HH24:MI:SS
and change the field name to *_ansi so that it cannot be confused with previous, improperly formatting fields. For example,to_char(timestamp,'MM/DD/YYYY HH:MI:SS') as foo_date_pretty
becomes
to_char(timestamp,'YYYY-MM-DD HH24:MI:SS') as foo_date_ansi
-
In TCL files where the date fields are used, convert the datetime from local server timezone, which is how it's stored in the database, to the user's timezone for display. Do this with the localizing function
lc_time_system_to_conn
:set foo_date_ansi [lc_time_system_to_conn $foo_date_ansi]
When a datetime will be written to the database, first convert it from the user's local time to the server's timezone with
lc_time_conn_to_system
. -
When a datetime field will be displayed, format it using the localizing function
lc_time_fmt
. lc_time_fmt takes two parameters, datetime and format code. Several format codes are usable for localization; they are placeholders that format dates with the appropriate codes for the user's locale. These codes are:%x, %X, %q, %Q, and %c.
set foo_date_pretty [lc_time_fmt $foo_date_ansi "%x %X"]
Use the
_pretty
version in your ADP page.-
%c: Long date and time (Mon November 18, 2002 12:00 AM)
-
%x: Short date (11/18/02)
-
%X: Time (12:00 AM)
-
%q: Long date without weekday (November 18, 2002)
-
%Q: Long date with weekday (Monday November 18, 2002)
The "q" format strings are OpenACS additions; the rest follow unix standards (see
man strftime
). -
-
-
Internationalize Numbers. To internationalize numbers, use
lc_numeric $value
, which formats the number using the appropriate decimal point and thousand separator for the locale. -
Internationalizing Forms.When coding forms, remember to use message keys for each piece of text that is user-visible, including form option labels and button labels.
-
Checking the Consistency of Catalog Files. This section describes how to check that the set of keys used in message lookups in tcl, adp, and info files and the set of keys in the catalog file are identical. The scripts below assume that message lookups in adp and info files are on the format #package_key.message_key#, and that message lookups in tcl files are always is done with one of the valid lookups described above. The script further assumes that you have perl installed and in your path. Run the script like this:
acs-lang/bin/check-catalog.sh package_key
where package_key is the key of the package that you want to test. If you don't provide the package_key argument then all packages with catalog files will be checked. The script will run its checks primarily on en_US xml catalog files.
-
Replace complicated keys with longer, simpler keys.When writing in one language, it is possible to create clever code to make correct text. In English, for example, you can put an
if
command at the end of a word which adds "s" if a count is anything but 1. This pluralizes nouns correctly based on the data. However, it is confusing to read and, when internationalized, may result in message keys that are both confusing and impossible to set correctly in some languages. While internationalizing, watch out that the automate converter does not create such keys. Also, refactor compound text as you encounter it.The automated system can easily get confused by tags within message texts, so that it tries to create two or three message keys for one long string with a tag in the middle. In these cases, uncheck those keys during the conversion and then edit the files directly. For example, this code:
<p class="form-help-text"><b>Invitations</b> are sent, when this wizard is completed and casting begins.</p>
has a bold tag which confuses the converter into thinking there are two message keys for the text beginning "Invitations ..." where there should be one:
Instead, we cancel those keys, edit the file manually, and put in a single temporary message tag:
<p class="form-help-text"> <#Invitations_are_sent <b>Invitations</b> are sent, when this wizard is completed and casting begins.#> </p>
Complex if statements may produce convoluted message keys that are very hard to localize. Rewrite these if statements. For example:
Select which case <if @simulation.casting_type@ eq "open">and role</if> to join, or create a new case for yourself. If you do not select a case <if @simulation.casting_type@ eq "open">and role</if> to join, you will be automatically assigned to a case <if @simulation.casting_type@ eq "open">and role</if> when the simulation begins.
... can be rewritten:
<if @simulation.casting_type@ eq "open"> Select which case and role to join, or create a new case for yourself. If you do not select a case and role to join, you will be automatically assigned to a case and role when the simulation begins. </if> <else> Select which case to join, or create a new case for yourself. If you do not select a case to join, you will be automatically assigned to a case when the simulation begins. </else>
Another example, where bugs are concatenated with a number:
<if @components.view_bugs_url@ not nil> <a href="@components.view_bugs_url@" title="View the @pretty_names.bugs@ for this component"> </if> @components.num_bugs@ <if @components.num_bugs@ eq 1> @pretty_names.bug@ </if> <else> @pretty_names.bugs@ </else> <if @components.view_bugs_url@ not nil> </a> </if> <if @components.view_bugs_url@ not nil> <a href="@components.view_bugs_url@" title="#bug-tracker.View_the_bug_fo_component#"> </if> @components.num_bugs@ <if @components.num_bugs@ eq 1> @pretty_names.bug@ </if> <else> @pretty_names.bugs@ </else> <if @components.view_bugs_url@ not nil> </a> </if>
It would probably be better to do this as something like:
<if @components.view_bugs_url@ not nil> <if @components.num_bugs@ eq 1> <a href="@components.view_bugs_url@" title="#bug-tracker.View_the_bug_fo_component#"><>#bug-tracker.one_bug#</a> </if><else> <a href="@components.view_bugs_url@" title="#bug-tracker.View_the_bug_fo_component#">#bug-tracker.N_bugs#</a> </else> </if>
-
Don't combine keys in display text.Converting a phrase from one language to another is usually more complicated than simply replacing each word with an equivalent. When several keys are concatenated, the resulting word order will not be correct for every language. Different languages may use expressions or idioms that don't match the phrase key-for-key. Create complete, distinct keys instead of building text from several keys. For example:
Original code:
multirow append links "New [bug_tracker::conn Bug]"
Problematic conversion:
multirow append links "[_ bug-tracker.New] [bug_tracker::conn Bug]"
Better conversion:
set bug_label [bug_tracker::conn Bug] multirow append links "[_ bug-tracker.New_Bug]" "${url_prefix}bug-add"
... and include the variable in the key:
"New %bug_label%"
. This gives translators more control over the phrase.In this example of bad i18n, full name is created by concatenating first and last name (admittedly this is pervasive in the toolkit):
<a href="@past_version.maintainer_url@" title="#bug-tracker.Email# @past_version.maintainer_email@"> @past_version.maintainer_first_names@ @past_version.maintainer_last_name@</a>
-
Avoid unnecessary duplicate keys.When phrases are exactly the same in several places, use a single key.
For common words such as Yes and No, you can use a library of keys at acs-kernel. For example, instead of using
myfirstpackage.Yes
, you can useacs-kernel.Yes
. You can also use the Message Key Search facility to find duplicates. Be careful, however, building up sentences from keys because grammar and other elements may not be consistent across different locales.Additional discussion: Re: Bug 961 ("Control Panel" displayed instead of "Administer"), Translation server upgraded, and Localization questions.
-
Don't internationalize internal code words.Many packages use code words or key words, such as "open" and "closed", which will never be shown to the user. They may match key values in the database, or be used in a switch or if statement. Don't change these.
For example, the original code is
workflow::case::add_log_data \ -entry_id $entry_id \ -key "resolution" \ -value [db_string select_resolution_code {}]
This is incorrectly internationalized to
workflow::case::add_log_data \ -entry_id $entry_id -key "[_ bug-tracker.resolution]" -value [db_string select_resolution_code {}]
But
resolution
is a keyword in a table and in the code, so this breaks the code. It should not have been internationalized at all. Here's another example of text that should not have been internationalized:{show_patch_status "open"}
It is broken if changed to
{show_patch_status "[_ bug-tracker.open]"}
-
Fix automatic truncated message keys.The automatic converter may create unique but crytic message keys. Watch out for these and replace them with more descriptive keys. For example:
<msg key="You">You can filter by this %component_name% by viisting %filter_url_string%</msg> <msg key="You_1">You do not have permission to map this patch to a bug. Only the submitter of the patch and users with write permission on this Bug Tracker project (package instance) may do so.</msg> <msg key="You_2">You do not have permission to edit this patch. Only the submitter of the patch and users with write permission on the Bug Tracker project (package instance) may do so.</msg>
These would be more useful if they were, "you_can_filter", "you_do_not_have_permission_to_map_this_patch", and "you_do_not_have_permission_to_edit_this_patch". Don't worry about exactly matching the english text, because that might change; instead try to capture the meaning of the phrase. Ask yourself, if I was a translator and didn't know how this application worked, would this key and text make translation easy for me?
Sometimes the automatic converter creates keys that don't semantically match their text. Fix these:
<msg key="Fix">for version</msg> <msg key="Fix_1">for</msg> <msg key="Fix_2">for Bugs</msg>
Another example:
Bug-tracker component maintainer"
was converted to"[_ bug-tracker.Bug-tracker]"
. Instead, it should bebug_tracker_component_maintainer
. -
Translations in Avoid "clever" message reuse.Translations may need to differ depending on the context in which the message appears.
-
Avoid plurals.Different languages create plurals differently. Try to avoid keys which will change based on the value of a number. OpenACS does not currently support internationalization of plurals. If you use two different keys, a plural and a singular form, your application will not localize properly for locales which use different rules or have more than two forms of plurals.
-
Quoting in the message catalog for tcl.Watch out for quoting and escaping when editing text that is also code. For example, the original string
set title "Patch \"$patch_summary\" is nice."
breaks if the message text retains all of the escaping that was in the tcl command:
<msg>Patch \"$patch_summary\" is nice.</msg>
When it becomes a key, it should be:
<msg>Patch "$patch_summary" is nice.</msg>
Also, some keys had %var;noquote%, which is not needed since those variables are not quoted (and in fact the variable won't even be recognized so you get the literal %var;noquote% in the output).
-
Be careful with curly brackets.Code within curly brackets isn't evaluated. TCL uses curly brackets as an alternative way to build lists. But TCL also uses curly brackets as an alternative to quotation marks for quoting text. So this original code
array set names { key "Pretty" ...}
... if converted to
array set names { key "[_bug-tracker.Pretty]" ...}
... won't work since the _ func will not be called. Instead, it should be
array set names [list key [_bug-tracker.Pretty] ...]